Merge "Update anomaly report UUID" into tm-qpr-dev am: 1d20bc7b47 am: 903b243744

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/20390283

Change-Id: I1a0b0dada13dc7473dc09c1c2e40cd84b71f004b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index fab53d1..eb16ba5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,9 +85,10 @@
         "android.hardware.radio-V1.6-java",
         "android.hardware.radio.config-V1-java",
         "android.hardware.radio.data-V1-java",
+        "android.hardware.radio.ims-V1-java",
         "android.hardware.radio.messaging-V1-java",
         "android.hardware.radio.modem-V1-java",
-        "android.hardware.radio.network-V1-java",
+        "android.hardware.radio.network-V2-java",
         "android.hardware.radio.sim-V1-java",
         "android.hardware.radio.voice-V1-java",
         "voip-common",
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 3a79cdc..1b4f565 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -180,6 +180,15 @@
 
     /* Unmetered networks information. */
     repeated UnmeteredNetworks unmetered_networks = 52;
+
+    /* Outgoing Short Code SMS statistics and information. */
+    repeated OutgoingShortCodeSms outgoing_short_code_sms = 53;
+
+    /* Timestamp of last outgoing_short_code_sms pull. */
+    optional int64 outgoing_short_code_sms_pull_timestamp_millis = 54;
+
+    /* Number of time the user toggled the data switch feature since the last collection. */
+    optional int32 auto_data_switch_toggle_count = 55;
 }
 
 // The canonical versions of the following enums live in:
@@ -271,6 +280,8 @@
     optional int32 retry_id = 13;
     optional int64 interval_millis = 14;
     optional int32 count = 15;
+    optional int32 send_error_code = 16;
+    optional int32 network_error_code = 17;
 
     // Internal use only
     optional int32 hashCode = 10001;
@@ -305,6 +316,7 @@
     optional int32 band_at_end = 19;
     repeated int32 handover_failure_causes = 20;
     repeated int32 handover_failure_rat = 21;
+    optional bool is_non_dds = 22;
 }
 
 message CellularServiceState {
@@ -517,3 +529,9 @@
     optional int32 carrier_id = 2;
     optional int64 unmetered_networks_bitmask = 3;
 }
+
+message OutgoingShortCodeSms {
+    optional int32 category = 1;
+    optional int32 xml_version = 2;
+    optional int32 short_code_sms_count = 3;
+}
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 6d3d711..b87728b 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -931,6 +931,9 @@
 
       /** Data switch caused by CBRS switch. */
       DATA_SWITCH_REASON_CBRS = 3;
+
+      /** Data switch caused by non-default SIM having better availability(e.g.signal). */
+      DATA_SWITCH_REASON_AUTO = 4;
     }
 
     /** The reason for data switch. */
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 972884a..f36161a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -114,6 +114,10 @@
     protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList();
     protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
     protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
+    protected RegistrantList mEmergencyNetworkScanRegistrants = new RegistrantList();
+    protected RegistrantList mConnectionSetupFailureRegistrants = new RegistrantList();
+    protected RegistrantList mNotifyAnbrRegistrants = new RegistrantList();
+    protected RegistrantList mTriggerImsDeregistrationRegistrants = new RegistrantList();
 
     @UnsupportedAppUsage
     protected Registrant mGsmSmsRegistrant;
@@ -1132,4 +1136,56 @@
     @Override
     public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
     }
+
+    /**
+     * Register for Emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    @Override
+    public void registerForEmergencyNetworkScan(Handler h, int what, Object obj) {
+        mEmergencyNetworkScanRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregister for Emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    @Override
+    public void unregisterForEmergencyNetworkScan(Handler h) {
+        mEmergencyNetworkScanRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+        mConnectionSetupFailureRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForConnectionSetupFailure(Handler h) {
+        mConnectionSetupFailureRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForNotifyAnbr(Handler h, int what, Object obj) {
+        mNotifyAnbrRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForNotifyAnbr(Handler h) {
+        mNotifyAnbrRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {
+        mTriggerImsDeregistrationRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForTriggerImsDeregistration(Handler h) {
+        mTriggerImsDeregistrationRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 3fdc444..4de16cb 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -35,6 +35,7 @@
     public static final int ERROR_CALLING_DISABLED = 5;
     public static final int ERROR_TOO_MANY_CALLS = 6;
     public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7;
+    public static final int ERROR_FDN_BLOCKED = 8;
 
     public
     CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallWaitingController.java b/src/java/com/android/internal/telephony/CallWaitingController.java
new file mode 100644
index 0000000..4940eb6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallWaitingController.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the change of the user setting of the call waiting service
+ */
+public class CallWaitingController extends Handler {
+
+    public static final String LOG_TAG = "CallWaitingCtrl";
+    private static final boolean DBG = false; /* STOPSHIP if true */
+
+    // Terminal-based call waiting is not supported. */
+    public static final int TERMINAL_BASED_NOT_SUPPORTED = -1;
+    // Terminal-based call waiting is supported but not activated. */
+    public static final int TERMINAL_BASED_NOT_ACTIVATED = 0;
+    // Terminal-based call waiting is supported and activated. */
+    public static final int TERMINAL_BASED_ACTIVATED = 1;
+
+    private static final int EVENT_SET_CALL_WAITING_DONE = 1;
+    private static final int EVENT_GET_CALL_WAITING_DONE = 2;
+    private static final int EVENT_REGISTERED_TO_NETWORK = 3;
+    private static final int EVENT_CARRIER_CONFIG_CHANGED = 4;
+
+    // Class to pack mOnComplete object passed by the caller
+    private static class Cw {
+        final boolean mEnable;
+        final Message mOnComplete;
+        final boolean mImsRegistered;
+
+        Cw(boolean enable, boolean imsRegistered, Message onComplete) {
+            mEnable = enable;
+            mOnComplete = onComplete;
+            mImsRegistered = imsRegistered;
+        }
+    }
+
+    @VisibleForTesting
+    public static final String PREFERENCE_TBCW = "terminal_based_call_waiting";
+    @VisibleForTesting
+    public static final String KEY_SUB_ID = "subId";
+    @VisibleForTesting
+    public static final String KEY_STATE = "state";
+    @VisibleForTesting
+    public static final String KEY_CS_SYNC = "cs_sync";
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent == null) {
+                return;
+            }
+            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+                Bundle bundle = intent.getExtras();
+                if (bundle == null) {
+                    return;
+                }
+                int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
+                        SubscriptionManager.INVALID_PHONE_INDEX);
+
+                if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                    loge("onReceive ACTION_CARRIER_CONFIG_CHANGED invalid slotId "
+                            + slotId);
+                    return;
+                }
+
+                if (slotId == mPhone.getPhoneId()) {
+                    sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
+                }
+            }
+        }
+    };
+
+    private boolean mSupportedByImsService = false;
+    private boolean mValidSubscription = false;
+
+    // The user's last setting of terminal-based call waiting
+    private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED;
+
+    private int mSyncPreference = CALL_WAITING_SYNC_NONE;
+    private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    private boolean mCsEnabled = false;
+    private boolean mRegisteredForNetworkAttach = false;
+    private boolean mImsRegistered = false;
+
+    private final GsmCdmaPhone mPhone;
+    private final ServiceStateTracker mSST;
+    private final Context mContext;
+
+    // Constructors
+    public CallWaitingController(GsmCdmaPhone phone) {
+        mPhone = phone;
+        mSST = phone.getServiceStateTracker();
+        mContext = phone.getContext();
+    }
+
+    private void initialize() {
+        mContext.registerReceiver(mReceiver, new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+        int phoneId = mPhone.getPhoneId();
+        int subId = mPhone.getSubId();
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+        mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+        mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE);
+
+        logi("initialize phoneId=" + phoneId
+                + ", lastSubId=" + mLastSubId + ", subId=" + subId
+                + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference
+                + ", csEnabled=" + mCsEnabled);
+    }
+
+    /**
+     * Returns the cached user setting.
+     *
+     * Possible values are
+     * {@link #TERMINAL_BASED_NOT_SUPPORTED},
+     * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and
+     * {@link #TERMINAL_BASED_ACTIVATED}.
+     *
+     * @param forCsOnly indicates the caller expects the result for CS calls only
+     */
+    @VisibleForTesting
+    public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            return TERMINAL_BASED_NOT_SUPPORTED;
+        }
+        if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED;
+        return mCallWaitingState;
+    }
+
+    /**
+     * Serves the user's requests to interrogate the call waiting service
+     *
+     * @return true when terminal-based call waiting is supported, otherwise false
+     */
+    @VisibleForTesting
+    public synchronized boolean getCallWaiting(@Nullable Message onComplete) {
+        if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+        logi("getCallWaiting " + mCallWaitingState);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Interrogate CW in CS network
+            if (!mCsEnabled) {
+                // skip interrogation if CS is not available and IMS is registered
+                if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+                    Cw cw = new Cw(false, isImsRegistered(), onComplete);
+                    Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+                    mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+                    return true;
+                }
+            }
+        }
+
+        if (mSyncPreference == CALL_WAITING_SYNC_NONE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                || isSyncImsOnly()) {
+            sendGetCallWaitingResponse(onComplete);
+            return true;
+        } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            Cw cw = new Cw(false, isImsRegistered(), onComplete);
+            Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+            mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Serves the user's requests to set the call waiting service
+     *
+     * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*.
+     * @return true when terminal-based call waiting is supported, otherwise false
+     */
+    @VisibleForTesting
+    public synchronized boolean setCallWaiting(boolean enable,
+            int serviceClass, @Nullable Message onComplete) {
+        if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+        if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false;
+
+        logi("setCallWaiting enable=" + enable + ", service=" + serviceClass);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Enable CW in the CS network
+            if (!mCsEnabled && enable) {
+                if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+                    Cw cw = new Cw(true, isImsRegistered(), onComplete);
+                    Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+                    mPhone.mCi.setCallWaiting(true, serviceClass, resp);
+                    return true;
+                } else {
+                    // CS network is not available, however, IMS is registered.
+                    // Enabling the service in the CS network will be delayed.
+                    registerForNetworkAttached();
+                }
+            }
+        }
+
+        if (mSyncPreference == CALL_WAITING_SYNC_NONE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                || isSyncImsOnly()) {
+            updateState(
+                    enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+
+            sendToTarget(onComplete, null, null);
+            return true;
+        } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            Cw cw = new Cw(enable, isImsRegistered(), onComplete);
+            Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+            mPhone.mCi.setCallWaiting(enable, serviceClass, resp);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_SET_CALL_WAITING_DONE:
+                onSetCallWaitingDone((AsyncResult) msg.obj);
+                break;
+            case EVENT_GET_CALL_WAITING_DONE:
+                onGetCallWaitingDone((AsyncResult) msg.obj);
+                break;
+            case EVENT_REGISTERED_TO_NETWORK:
+                onRegisteredToNetwork();
+                break;
+            case EVENT_CARRIER_CONFIG_CHANGED:
+                onCarrierConfigChanged();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private synchronized void onSetCallWaitingDone(AsyncResult ar) {
+        if (ar.userObj == null) {
+            // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+            if (DBG) logd("onSetCallWaitingDone to sync on network attached");
+            if (ar.exception == null) {
+                updateSyncState(true);
+            } else {
+                loge("onSetCallWaitingDone e=" + ar.exception);
+            }
+            return;
+        }
+
+        if (!(ar.userObj instanceof Cw)) {
+            // Unexpected state
+            if (DBG) logd("onSetCallWaitingDone unexpected result");
+            return;
+        }
+
+        if (DBG) logd("onSetCallWaitingDone");
+        Cw cw = (Cw) ar.userObj;
+
+        if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            // do not synchronize service state between CS and IMS
+            sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+            return;
+        }
+
+        if (ar.exception == null) {
+            if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+                updateSyncState(true);
+            }
+            updateState(
+                    cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+        } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            if (cw.mImsRegistered) {
+                // IMS is registered. Do not notify error.
+                // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+                updateState(TERMINAL_BASED_ACTIVATED);
+                sendToTarget(cw.mOnComplete, null, null);
+                return;
+            }
+        }
+        sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+    }
+
+    private synchronized void onGetCallWaitingDone(AsyncResult ar) {
+        if (ar.userObj == null) {
+            // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+            if (DBG) logd("onGetCallWaitingDone to sync on network attached");
+            boolean enabled = false;
+            if (ar.exception == null) {
+                //resp[0]: 1 if enabled, 0 otherwise
+                //resp[1]: bitwise ORs of SERVICE_CLASS_* constants
+                int[] resp = (int[]) ar.result;
+                if (resp != null && resp.length > 1) {
+                    enabled = (resp[0] == 1)
+                            && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+                } else {
+                    loge("onGetCallWaitingDone unexpected response");
+                }
+            } else {
+                loge("onGetCallWaitingDone e=" + ar.exception);
+            }
+            if (enabled) {
+                updateSyncState(true);
+            } else {
+                logi("onGetCallWaitingDone enabling CW service in CS network");
+                mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE,
+                        obtainMessage(EVENT_SET_CALL_WAITING_DONE));
+            }
+            unregisterForNetworkAttached();
+            return;
+        }
+
+        if (!(ar.userObj instanceof Cw)) {
+            // Unexpected state
+            if (DBG) logd("onGetCallWaitingDone unexpected result");
+            return;
+        }
+
+        if (DBG) logd("onGetCallWaitingDone");
+        Cw cw = (Cw) ar.userObj;
+
+        if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            // do not synchronize service state between CS and IMS
+            sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+            return;
+        }
+
+        if (ar.exception == null) {
+            int[] resp = (int[]) ar.result;
+            //resp[0]: 1 if enabled, 0 otherwise
+            //resp[1]: bitwise ORs of SERVICE_CLASS_
+            if (resp == null || resp.length < 2) {
+                logi("onGetCallWaitingDone unexpected response");
+                if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                    // no exception but unexpected response, local setting is preferred.
+                    sendGetCallWaitingResponse(cw.mOnComplete);
+                } else {
+                    sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+                }
+                return;
+            }
+
+            boolean enabled = resp[0] == 1
+                    && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+
+            if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                updateSyncState(enabled);
+
+                if (!enabled && !cw.mImsRegistered) {
+                    // IMS is not registered, change the local setting
+                    logi("onGetCallWaitingDone CW in CS network is disabled.");
+                    updateState(TERMINAL_BASED_NOT_ACTIVATED);
+                }
+
+                // return the user setting saved
+                sendGetCallWaitingResponse(cw.mOnComplete);
+                return;
+            }
+            updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+        } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Got an exception
+            if (cw.mImsRegistered) {
+                // queryCallWaiting failed. However, IMS is registered. Do not notify error.
+                // return the user setting saved
+                logi("onGetCallWaitingDone get an exception, but IMS is registered");
+                sendGetCallWaitingResponse(cw.mOnComplete);
+                return;
+            }
+        }
+        sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+    }
+
+    private void sendToTarget(Message onComplete, Object result, Throwable exception) {
+        if (onComplete != null) {
+            AsyncResult.forMessage(onComplete, result, exception);
+            onComplete.sendToTarget();
+        }
+    }
+
+    private void sendGetCallWaitingResponse(Message onComplete) {
+        if (onComplete != null) {
+            int serviceClass = SERVICE_CLASS_NONE;
+            if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) {
+                serviceClass = SERVICE_CLASS_VOICE;
+            }
+            sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null);
+        }
+    }
+
+    private synchronized void onRegisteredToNetwork() {
+        if (mCsEnabled) return;
+
+        if (DBG) logd("onRegisteredToNetwork");
+
+        mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE,
+                obtainMessage(EVENT_GET_CALL_WAITING_DONE));
+    }
+
+    private synchronized void onCarrierConfigChanged() {
+        int subId = mPhone.getSubId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            logi("onCarrierConfigChanged invalid subId=" + subId);
+
+            mValidSubscription = false;
+            unregisterForNetworkAttached();
+            return;
+        }
+
+        CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+        PersistableBundle b = configManager.getConfigForSubId(subId);
+
+        updateCarrierConfig(subId, b, false);
+
+        logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) {
+            if (!mCsEnabled) {
+                registerForNetworkAttached();
+            }
+        }
+    }
+
+    /**
+     * @param ignoreSavedState only used for test
+     */
+    @VisibleForTesting
+    public void updateCarrierConfig(int subId, PersistableBundle b, boolean ignoreSavedState) {
+        mValidSubscription = true;
+
+        if (b == null) return;
+
+        boolean supportsTerminalBased = false;
+        int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY);
+        if (services != null) {
+            for (int service : services) {
+                if (service == SUPPLEMENTARY_SERVICE_CW) {
+                    supportsTerminalBased = true;
+                }
+            }
+        }
+        int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+                CALL_WAITING_SYNC_FIRST_CHANGE);
+        boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
+        int defaultState = supportsTerminalBased
+                ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED)
+                : TERMINAL_BASED_NOT_SUPPORTED;
+        int savedState = getSavedState(subId);
+
+        if (DBG) {
+            logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId()
+                    + ", subId=" + subId + ", support=" + supportsTerminalBased
+                    + ", sync=" + syncPreference + ", default=" + defaultState
+                    + ", savedState=" + savedState);
+        }
+
+        int desiredState = savedState;
+
+        if (ignoreSavedState) {
+            desiredState = defaultState;
+        } else if ((mLastSubId != subId)
+                && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                        || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) {
+            desiredState = defaultState;
+        } else {
+            if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) {
+                desiredState = TERMINAL_BASED_NOT_SUPPORTED;
+            } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) {
+                desiredState = defaultState;
+            }
+        }
+
+        updateState(desiredState, syncPreference, ignoreSavedState);
+    }
+
+    private void updateState(int state) {
+        updateState(state, mSyncPreference, false);
+    }
+
+    private void updateState(int state, int syncPreference, boolean ignoreSavedState) {
+        int subId = mPhone.getSubId();
+
+        if (mLastSubId == subId
+                && mCallWaitingState == state
+                && mSyncPreference == syncPreference
+                && (!ignoreSavedState)) {
+            return;
+        }
+
+        int phoneId = mPhone.getPhoneId();
+
+        logi("updateState phoneId=" + phoneId
+                + ", subId=" + subId + ", state=" + state
+                + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState);
+
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putInt(KEY_SUB_ID + phoneId, subId);
+        editor.putInt(KEY_STATE + subId, state);
+        editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
+        editor.apply();
+
+        mCallWaitingState = state;
+        mLastSubId = subId;
+        mSyncPreference = syncPreference;
+        if (mLastSubId != subId) {
+            mCsEnabled = false;
+        }
+
+        mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState);
+    }
+
+    private int getSavedState(int subId) {
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+        int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+
+        logi("getSavedState subId=" + subId + ", state=" + state);
+
+        return state;
+    }
+
+    private void updateSyncState(boolean enabled) {
+        int phoneId = mPhone.getPhoneId();
+
+        logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled);
+
+        mCsEnabled = enabled;
+    }
+
+    /**
+     * @return whether the service is enabled in the CS network
+     */
+    @VisibleForTesting
+    public boolean getSyncState() {
+        return mCsEnabled;
+    }
+
+    private boolean isCircuitSwitchedNetworkAvailable() {
+        logi("isCircuitSwitchedNetworkAvailable="
+                + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE));
+        return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE;
+    }
+
+    private boolean isImsRegistered() {
+        logi("isImsRegistered " + mImsRegistered);
+        return mImsRegistered;
+    }
+
+    /**
+     * Sets the registration state of IMS service.
+     */
+    public synchronized void setImsRegistrationState(boolean registered) {
+        logi("setImsRegistrationState prev=" + mImsRegistered
+                + ", new=" + registered);
+        mImsRegistered = registered;
+    }
+
+    private void registerForNetworkAttached() {
+        logi("registerForNetworkAttached");
+        if (mRegisteredForNetworkAttach) return;
+
+        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+        mRegisteredForNetworkAttach = true;
+    }
+
+    private void unregisterForNetworkAttached() {
+        logi("unregisterForNetworkAttached");
+        if (!mRegisteredForNetworkAttach) return;
+
+        mSST.unregisterForNetworkAttached(this);
+        removeMessages(EVENT_REGISTERED_TO_NETWORK);
+        mRegisteredForNetworkAttach = false;
+    }
+
+    /**
+     * Sets whether the device supports the terminal-based call waiting.
+     * Only for test
+     */
+    @VisibleForTesting
+    public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) {
+        if (mSupportedByImsService == supported) return;
+
+        logi("setTerminalBasedCallWaitingSupported " + supported);
+
+        mSupportedByImsService = supported;
+
+        if (supported) {
+            initialize();
+            onCarrierConfigChanged();
+        } else {
+            mContext.unregisterReceiver(mReceiver);
+            updateState(TERMINAL_BASED_NOT_SUPPORTED);
+        }
+    }
+
+    /**
+     * Notifies that the UE has attached to the network
+     * Only for test
+     */
+    @VisibleForTesting
+    public void notifyRegisteredToNetwork() {
+        sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK);
+    }
+
+    private boolean isSyncImsOnly() {
+        return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered);
+    }
+
+    /**
+     * Dump this instance into a readable format for dumpsys usage.
+     */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.increaseIndent();
+        pw.println("CallWaitingController:");
+        pw.println(" mSupportedByImsService=" + mSupportedByImsService);
+        pw.println(" mValidSubscription=" + mValidSubscription);
+        pw.println(" mCallWaitingState=" + mCallWaitingState);
+        pw.println(" mSyncPreference=" + mSyncPreference);
+        pw.println(" mLastSubId=" + mLastSubId);
+        pw.println(" mCsEnabled=" + mCsEnabled);
+        pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach);
+        pw.println(" mImsRegistered=" + mImsRegistered);
+        pw.decreaseIndent();
+    }
+
+    private void loge(String msg) {
+        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+
+    private void logi(String msg) {
+        Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+
+    private void logd(String msg) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 3144229..b3afb66 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -86,6 +86,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -610,10 +611,10 @@
         List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
         for (Signature signature : signatures) {
             byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
-            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase());
+            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
 
             byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
-            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase());
+            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
         }
 
         mInstalledPackageCerts.put(pkg.packageName, certs);
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index ec6a5a0..7f1d349 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -54,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id
@@ -193,7 +194,7 @@
     /**
      * This is triggered from SubscriptionInfoUpdater after sim state change.
      * The sequence of sim loading would be
-     *  1. ACTION_SUBINFO_CONTENT_CHANGE
+     *  1. OnSubscriptionsChangedListener
      *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
      *  /ACTION_SIM_APPLICATION_STATE_CHANGED
      *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
@@ -751,7 +752,8 @@
         // Ideally we should do full string match. However due to SIM manufacture issues
         // gid from some SIM might has garbage tail.
         private boolean gidMatch(String gidFromSim, String gid) {
-            return (gidFromSim != null) && gidFromSim.toLowerCase().startsWith(gid.toLowerCase());
+            return (gidFromSim != null) && gidFromSim.toLowerCase(Locale.ROOT)
+                    .startsWith(gid.toLowerCase(Locale.ROOT));
         }
 
         private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 4253905..19a585d 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -500,14 +500,6 @@
             final String rplmn = regResult.registeredPlmn;
             final int reasonForDenial = regResult.reasonForDenial;
 
-            if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
-                    && reasonForDenial
-                    == android.hardware.radio.network.RegistrationFailCause.NONE) {
-                AnomalyReporter.reportAnomaly(
-                        UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f20"),
-                            "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
-            }
-
             int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
             if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
                 networkType = TelephonyManager.NETWORK_TYPE_LTE;
@@ -590,14 +582,6 @@
             networkType =
                     getNetworkTypeForCellIdentity(networkType, cellIdentity, mPhone.getCarrierId());
 
-            if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
-                    && reasonForDenial
-                    == android.hardware.radio.network.RegistrationFailCause.NONE) {
-                AnomalyReporter.reportAnomaly(
-                        UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f20"),
-                            "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
-            }
-
             // Conditional parameters for specific RANs
             boolean cssSupported = false;
             int roamingIndicator = 0;
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 27cedfe..98de4be 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -25,14 +25,17 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.WorkSource;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.NetworkSliceInfo;
@@ -40,6 +43,7 @@
 import android.telephony.emergency.EmergencyNumber;
 
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.IccCardStatus;
@@ -124,6 +128,15 @@
     static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM     = 39;
     static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM           = 96;
 
+    /** IMS voice capability */
+    int IMS_MMTEL_CAPABILITY_VOICE = 1 << 0;
+    /** IMS video capability */
+    int IMS_MMTEL_CAPABILITY_VIDEO = 1 << 1;
+    /** IMS SMS capability */
+    int IMS_MMTEL_CAPABILITY_SMS = 1 << 2;
+    /** IMS RCS capabilities */
+    int IMS_RCS_CAPABILITIES = 1 << 3;
+
     //***** Methods
 
     /**
@@ -2186,11 +2199,21 @@
 
     /**
      * @return the radio hal version
+     * @deprecated use {@link #getHalVersion(int)}
      */
+    @Deprecated
     default HalVersion getHalVersion() {
         return HalVersion.UNKNOWN;
     }
 
+    /**
+     * @param service indicate the service id to query.
+     * @return the hal version of a specific service
+     */
+    default HalVersion getHalVersion(@HalService int service) {
+        return HalVersion.UNKNOWN;
+    }
+
    /**
      * Sets user selected subscription at Modem.
      *
@@ -2747,6 +2770,54 @@
      public void unregisterForSimPhonebookRecordsReceived(Handler h);
 
     /**
+     * Registers for notifications when connection setup fails.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForConnectionSetupFailure(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for notifications when connection setup fails.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForConnectionSetupFailure(Handler h) {}
+
+    /**
+     * Registers for notifications when ANBR is received form the network.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForNotifyAnbr(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for notifications when ANBR is received form the network.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForNotifyAnbr(Handler h) {}
+
+    /**
+     * Registers for IMS deregistration trigger from modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for IMS deregistration trigger from modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForTriggerImsDeregistration(Handler h) {}
+
+    /**
      * Set the UE's usage setting.
      *
      * @param result Callback message containing the success or failure status.
@@ -2761,4 +2832,123 @@
      * @param result Callback message containing the usage setting (or a failure status).
      */
     default void getUsageSetting(Message result) {}
+
+    /**
+     * Sets the emergency mode.
+     *
+     * @param emcMode Defines the radio emergency mode type.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+            @Nullable Message result) {}
+
+    /**
+     * Triggers an emergency network scan.
+     *
+     * @param accessNetwork Contains the list of access network types to be prioritized
+     *        during emergency scan. The 1st entry has the highest priority.
+     * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+     *        full service scan or both.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void triggerEmergencyNetworkScan(
+            @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+            @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {}
+
+    /**
+     * Cancels ongoing emergency network scan.
+     *
+     * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+     *        If {@code true}, then the modem shall start the new scan from the beginning,
+     *        otherwise the modem shall resume from the last search.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {}
+
+    /**
+     * Exits ongoing emergency mode.
+     *
+     * @param result Callback message containing the success or failure status.
+     */
+    default void exitEmergencyMode(@Nullable Message result) {}
+
+    /**
+     * Registers for emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForEmergencyNetworkScan(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForEmergencyNetworkScan(@NonNull Handler h) {}
+
+    /**
+     * Provides a list of SRVCC call information to radio
+     *
+     * @param srvccConnections the list of connections.
+     */
+    default void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {}
+
+    /**
+     * Updates the IMS registration information to the radio.
+     *
+     * @param state The current IMS registration state.
+     * @param accessNetworkType The type of underlying radio access network used.
+     * @param suggestedAction The expected action that modem should perform.
+     * @param capabilities IMS capabilities such as VOICE, VIDEO and SMS.
+     */
+    default void updateImsRegistrationInfo(int state,
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            int suggestedAction, int capabilities, Message result) {}
+
+    /**
+     * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+     *
+     * @param token A nonce to identify the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     * @param accessNetworkType The type of underlying radio access network used.
+     */
+    default void startImsTraffic(int token, int trafficType,
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            Message result) {}
+
+    /**
+     * Notifies IMS traffic has been stopped.
+     *
+     * @param token The token assigned by startImsTraffic.
+     */
+    default void stopImsTraffic(int token, Message result) {}
+
+    /**
+     * Triggers the UE initiated EPS fallback procedure.
+     *
+     * @param reason Specifies the reason for EPS fallback.
+     */
+    default void triggerEpsFallback(int reason, Message result) {}
+
+    /**
+     * Triggers radio to send ANBRQ message to the network.
+     *
+     * @param mediaType Media type is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The bit rate requested by the opponent UE.
+     */
+    default void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, Message result) {}
+
+    /**
+     * Set the UE's ability to accept/reject null ciphered and/or null integrity-protected
+     * connections.
+     *
+     * @param result Callback message containing the success or failure status.
+     * @param enabled true to allow null ciphered and/or null integrity-protected connections,
+     * false to disallow.
+     */
+    default void setNullCipherAndIntegrityEnabled(Message result, boolean enabled) {}
 }
diff --git a/src/java/com/android/internal/telephony/DataIndication.java b/src/java/com/android/internal/telephony/DataIndication.java
index 3467955..205c4d8 100644
--- a/src/java/com/android/internal/telephony/DataIndication.java
+++ b/src/java/com/android/internal/telephony/DataIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
@@ -53,7 +55,7 @@
      */
     public void dataCallListChanged(int indicationType,
             android.hardware.radio.data.SetupDataCallResult[] dcList) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
         ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList);
@@ -68,7 +70,7 @@
      */
     public void keepaliveStatus(int indicationType,
             android.hardware.radio.data.KeepaliveStatus halStatus) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(
@@ -87,7 +89,7 @@
      * @param pco New PcoData
      */
     public void pcoData(int indicationType, android.hardware.radio.data.PcoDataInfo pco) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
         PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId, pco.contents);
 
@@ -104,7 +106,7 @@
      */
     public void unthrottleApn(int indicationType, android.hardware.radio.data.DataProfileInfo dpi)
             throws RemoteException {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
         DataProfile response = RILUtils.convertToDataProfile(dpi);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response);
@@ -120,7 +122,7 @@
      */
     public void slicingConfigChanged(int indicationType,
             android.hardware.radio.data.SlicingConfig slicingConfig) throws RemoteException {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
         }
diff --git a/src/java/com/android/internal/telephony/DataResponse.java b/src/java/com/android/internal/telephony/DataResponse.java
index 7cfe13b..bef1da7 100644
--- a/src/java/com/android/internal/telephony/DataResponse.java
+++ b/src/java/com/android/internal/telephony/DataResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.data.IRadioDataResponse;
@@ -51,7 +53,7 @@
      * @param id The pdu session id allocated
      */
     public void allocatePduSessionIdResponse(RadioResponseInfo responseInfo, int id) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
                 RadioResponse.sendMessageResponse(rr.mResult, id);
@@ -64,14 +66,14 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void cancelHandoverResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -80,7 +82,7 @@
      */
     public void getDataCallListResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.SetupDataCallResult[] dataCallResultList) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             ArrayList<DataCallResponse> response =
@@ -98,7 +100,7 @@
      */
     public void getSlicingConfigResponse(RadioResponseInfo responseInfo,
                 android.hardware.radio.data.SlicingConfig slicingConfig) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
@@ -113,35 +115,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void releasePduSessionIdResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataAllowedResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataProfileResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataThrottlingResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setInitialAttachApnResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -150,7 +152,7 @@
      */
     public void setupDataCallResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.SetupDataCallResult setupDataCallResult) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             DataCallResponse response = RILUtils.convertHalDataCallResult(setupDataCallResult);
@@ -165,7 +167,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startHandoverResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -175,7 +177,7 @@
     public void startKeepaliveResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.KeepaliveStatus keepaliveStatus) {
 
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr == null) return;
 
         KeepaliveStatus ret = null;
@@ -214,7 +216,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr == null) return;
 
         try {
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 3d63a29..ecc6208 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -20,6 +20,7 @@
 import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
 import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
@@ -669,7 +670,7 @@
                 LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN);
         mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
                 LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000);
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+        if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
                     LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.NGRAN);
         }
diff --git a/src/java/com/android/internal/telephony/FdnUtils.java b/src/java/com/android/internal/telephony/FdnUtils.java
new file mode 100644
index 0000000..aa2bcfd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/FdnUtils.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.text.TextUtils;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+
+/**
+ * This is a basic utility class for common functions related to Fixed Dialing Numbers
+ * designed as per 3GPP 22.101.
+ */
+public class FdnUtils {
+    private static final boolean VDBG = false;
+    private static final String LOG_TAG = FdnUtils.class.getSimpleName();
+
+    /**
+     * The following function checks if dialed number is blocked due to FDN.
+     *
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @param dialStr dialed phone number
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if dialStr is blocked due to FDN check.
+     */
+    public static boolean isNumberBlockedByFDN(int phoneId, String dialStr,
+            String defaultCountryIso) {
+        if (!isFdnEnabled(phoneId)) {
+            return false;
+        }
+
+        ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+        return !isFDN(dialStr, defaultCountryIso, fdnList);
+    }
+
+    /**
+     * Checks if FDN is enabled
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @return {@code true} if FDN is enabled
+     */
+    public static boolean isFdnEnabled(int phoneId) {
+        UiccCardApplication app = getUiccCardApplication(phoneId);
+        if (app == null || (!app.getIccFdnAvailable())) {
+            return false;
+        }
+
+        return app.getIccFdnEnabled();
+    }
+
+    /**
+     * If FDN is enabled, check to see if the given supplementary service control strings are
+     * blocked due to FDN.
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @param controlStrings control strings associated with the supplementary service request
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if the FDN list does not contain any of the control strings.
+     */
+    public static boolean isSuppServiceRequestBlockedByFdn(int phoneId,
+            ArrayList<String> controlStrings, String defaultCountryIso) {
+        if (!isFdnEnabled(phoneId)) {
+            return false;
+        }
+
+        ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+        for(String controlString : controlStrings) {
+            if(isFDN(controlString, defaultCountryIso, fdnList)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Checks if dialStr is part of FDN list.
+     *
+     * @param fdnList List of all FDN records associated with a sim card
+     * @param dialStr dialed phone number
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if dialStr is present in the fdnList.
+     */
+    @VisibleForTesting
+    public static boolean isFDN(String dialStr, String defaultCountryIso,
+            ArrayList<AdnRecord> fdnList) {
+        if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) {
+            Rlog.w(LOG_TAG, "isFDN: unexpected null value");
+            return false;
+        }
+
+        // Parse the dialStr and convert it to E164 format
+        String dialStrE164 = null;
+        String dialStrNational = null;
+        final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+        try {
+            PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso);
+            dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164);
+            dialStrNational = String.valueOf(phoneNumber.getNationalNumber());
+        } catch (NumberParseException ignored) {
+            Rlog.w(LOG_TAG, "isFDN: could not parse dialStr");
+        }
+
+        /**
+         * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber
+         * E.g.1: returns true if fdnNumber="123" and dialStr="12345"
+         * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345"
+         */
+        for (AdnRecord fdn: fdnList) {
+            String fdnNumber = fdn.getNumber();
+            if (TextUtils.isEmpty(fdnNumber)) {
+                continue;
+            }
+
+            if(!TextUtils.isEmpty(dialStrE164)) {
+                if(dialStrE164.startsWith(fdnNumber)) {
+                    return true;
+                }
+            }
+
+            if(!TextUtils.isEmpty(dialStrNational)) {
+                if (dialStrNational.startsWith(fdnNumber)) {
+                    return true;
+                }
+            }
+
+            if (dialStr.startsWith(fdnNumber)) {
+                return true;
+            }
+        }
+
+        if (VDBG) {
+            Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list");
+        }
+        return false;
+    }
+
+    private static ArrayList<AdnRecord> getFdnList(int phoneId) {
+        UiccCardApplication app = getUiccCardApplication(phoneId);
+        if (app == null) {
+            return null;
+        }
+
+        IccRecords iccRecords = app.getIccRecords();
+        if (iccRecords == null) {
+            return null;
+        }
+
+        AdnRecordCache adnRecordCache = iccRecords.getAdnCache();
+        if(adnRecordCache == null) {
+            return null;
+        }
+
+        return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN);
+    }
+
+    private static UiccCardApplication getUiccCardApplication(int phoneId) {
+        UiccProfile uiccProfile = UiccController.getInstance()
+                .getUiccProfileForPhone(phoneId);
+        if (uiccProfile == null) {
+            return null;
+        }
+
+        return uiccProfile.getApplication(UiccController.APP_FAM_3GPP);
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 3293558..263bfeb 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -182,11 +182,6 @@
             mCi.unregisterForCallWaitingInfo(this);
             // Prior to phone switch to GSM, if CDMA has any emergency call
             // data will be in disabled state, after switching to GSM enable data.
-            if (mIsInEmergencyCall) {
-                if (!mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
-                }
-            }
         } else {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
             mPendingCallInEcm = false;
@@ -400,9 +395,6 @@
     //CDMA
     public void setIsInEmergencyCall() {
         mIsInEmergencyCall = true;
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.getDataEnabledSettings().setInternalDataEnabled(false);
-        }
         mPhone.notifyEmergencyCallRegistrants(true);
         mPhone.sendEmergencyCallStateChange(true);
     }
@@ -967,6 +959,8 @@
                             } else {
                                 newUnknownConnectionCdma = mConnections[i];
                             }
+                        } else if (hangupWaitingCallSilently(i)) {
+                            return;
                         }
                     }
                 }
@@ -1018,6 +1012,9 @@
 
                 if (mConnections[i].getCall() == mRingingCall) {
                     newRinging = mConnections[i];
+                    if (hangupWaitingCallSilently(i)) {
+                        return;
+                    }
                 } // else something strange happened
                 hasNonHangupStateChanged = true;
             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
@@ -1754,9 +1751,6 @@
             }
             if (!inEcm) {
                 // Re-initiate data connection
-                if (!mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
-                }
                 mPhone.notifyEmergencyCallRegistrants(false);
             }
             mPhone.sendEmergencyCallStateChange(false);
@@ -1909,4 +1903,22 @@
     public void cleanupCalls() {
         pollCallsWhenSafe();
     }
+
+    private boolean hangupWaitingCallSilently(int index) {
+        if (index < 0 || index >= mConnections.length) return false;
+
+        GsmCdmaConnection newRinging = mConnections[index];
+        if (newRinging == null) return false;
+
+        if ((mPhone.getTerminalBasedCallWaitingState(true)
+                        == CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED)
+                && (newRinging.getState() == Call.State.WAITING)) {
+            Rlog.d(LOG_TAG, "hangupWaitingCallSilently");
+            newRinging.dispose();
+            mConnections[index] = null;
+            mCi.hangupWaitingOrBackground(obtainCompleteMessage());
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index ff8412f..77b0909 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -29,6 +29,9 @@
 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import static com.android.internal.telephony.SsDomainController.SS_CLIP;
+import static com.android.internal.telephony.SsDomainController.SS_CLIR;
+import static com.android.internal.telephony.SsDomainController.SS_CW;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -64,7 +67,6 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.BarringInfo;
@@ -82,7 +84,6 @@
 import android.telephony.TelephonyManager;
 import android.telephony.UiccAccessRule;
 import android.telephony.UssdResponse;
-import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -94,11 +95,9 @@
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.GsmMmiCode;
+import com.android.internal.telephony.gsm.SsData;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
@@ -131,6 +130,8 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -263,6 +264,8 @@
     private boolean mResetModemOnRadioTechnologyChange = false;
     private boolean mSsOverCdmaSupported = false;
 
+    private SsDomainController mSsDomainController;
+
     private int mRilVersion;
     private boolean mBroadcastEmergencyCallStateChanges = false;
     private @ServiceState.RegState int mTelecomVoiceServiceStateOverride =
@@ -271,12 +274,11 @@
     private CarrierKeyDownloadManager mCDM;
     private CarrierInfoManager mCIM;
 
-    private final SettingsObserver mSettingsObserver;
-
     private final ImsManagerFactory mImsManagerFactory;
     private final CarrierPrivilegesTracker mCarrierPrivilegesTracker;
 
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
+    private final CallWaitingController mCallWaitingController;
 
     // Constructors
 
@@ -316,10 +318,6 @@
         mAccessNetworksManager = mTelephonyComponentFactory
                 .inject(AccessNetworksManager.class.getName())
                 .makeAccessNetworksManager(this, getLooper());
-        if (!isUsingNewDataStack()) {
-            mTransportManager = mTelephonyComponentFactory.inject(TransportManager.class.getName())
-                    .makeTransportManager(this);
-        }
         // SST/DSM depends on SSC, so SSC is instanced before SST/DSM
         mSignalStrengthController = mTelephonyComponentFactory.inject(
                 SignalStrengthController.class.getName()).makeSignalStrengthController(this);
@@ -328,10 +326,6 @@
         mEmergencyNumberTracker = mTelephonyComponentFactory
                 .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
                         this, this.mCi);
-        if (!isUsingNewDataStack()) {
-            mDataEnabledSettings = mTelephonyComponentFactory
-                    .inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this);
-        }
         mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
                 .makeDeviceStateMonitor(this);
 
@@ -340,20 +334,9 @@
         mDisplayInfoController = mTelephonyComponentFactory.inject(
                 DisplayInfoController.class.getName()).makeDisplayInfoController(this);
 
-        if (isUsingNewDataStack()) {
-            mDataNetworkController = mTelephonyComponentFactory.inject(
-                    DataNetworkController.class.getName())
-                    .makeDataNetworkController(this, getLooper());
-        } else {
-            // DcTracker uses ServiceStateTracker and DisplayInfoController so needs to be created
-            // after they are instantiated
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                DcTracker dcTracker = mTelephonyComponentFactory.inject(DcTracker.class.getName())
-                        .makeDcTracker(this, transport);
-                mDcTrackers.put(transport, dcTracker);
-                mAccessNetworksManager.registerDataThrottler(dcTracker.getDataThrottler());
-            }
-        }
+        mDataNetworkController = mTelephonyComponentFactory.inject(
+                DataNetworkController.class.getName())
+                .makeDataNetworkController(this, getLooper());
 
         mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
                 .makeCarrierResolver(this);
@@ -366,15 +349,6 @@
         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
         mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null);
 
-        // TODO: Remove SettingsObserver and provisioning events when DataEnabledSettings is removed
-        mSettingsObserver = new SettingsObserver(context, this);
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                EVENT_DEVICE_PROVISIONED_CHANGE);
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
-                EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE);
-
         SubscriptionController.getInstance().registerForUiccAppsEnabled(this,
                 EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false);
 
@@ -382,6 +356,8 @@
                 .inject(LinkBandwidthEstimator.class.getName())
                 .makeLinkBandwidthEstimator(this);
 
+        mCallWaitingController = new CallWaitingController(this);
+
         loadTtyMode();
 
         CallManager.getInstance().registerPhone(this);
@@ -398,6 +374,8 @@
         subMan.addOnSubscriptionsChangedListener(
                 new HandlerExecutor(this), mSubscriptionsChangedListener);
 
+        mSsDomainController = new SsDomainController(this);
+
         logd("GsmCdmaPhone: constructor: sub = " + mPhoneId);
     }
 
@@ -680,11 +658,6 @@
     }
 
     @Override
-    public TransportManager getTransportManager() {
-        return mTransportManager;
-    }
-
-    @Override
     public AccessNetworksManager getAccessNetworksManager() {
         return mAccessNetworksManager;
     }
@@ -736,83 +709,8 @@
     }
 
     @Override
-    public PhoneConstants.DataState getDataConnectionState(String apnType) {
-        PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED;
-
-        if (mSST == null) {
-            // Radio Technology Change is ongoing, dispose() and removeReferences() have
-            // already been called
-
-            ret = PhoneConstants.DataState.DISCONNECTED;
-        } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
-                && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
-                (isPhoneTypeGsm() && !apnType.equals(ApnSetting.TYPE_EMERGENCY_STRING)))) {
-            // If we're out of service, open TCP sockets may still work
-            // but no data will flow
-
-            // Emergency APN is available even in Out Of Service
-            // Pass the actual State of EPDN
-
-            ret = PhoneConstants.DataState.DISCONNECTED;
-        } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
-            int currentTransport = mAccessNetworksManager.getCurrentTransport(
-                    ApnSetting.getApnTypesBitmaskFromString(apnType));
-            if (getDcTracker(currentTransport) != null) {
-                switch (getDcTracker(currentTransport).getState(apnType)) {
-                    case CONNECTED:
-                    case DISCONNECTING:
-                        if (isDataSuspended()) {
-                            ret = PhoneConstants.DataState.SUSPENDED;
-                        } else {
-                            ret = PhoneConstants.DataState.CONNECTED;
-                        }
-                        break;
-                    case CONNECTING:
-                        ret = PhoneConstants.DataState.CONNECTING;
-                        break;
-                    default:
-                        ret = PhoneConstants.DataState.DISCONNECTED;
-                }
-            }
-        }
-
-        logd("getDataConnectionState apnType=" + apnType + " ret=" + ret);
-        return ret;
-    }
-
-    @Override
     public @DataActivityType int getDataActivityState() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().getDataActivity();
-        }
-        int ret = TelephonyManager.DATA_ACTIVITY_NONE;
-
-        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE
-                && getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            switch (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getActivity()) {
-                case DATAIN:
-                    ret = TelephonyManager.DATA_ACTIVITY_IN;
-                break;
-
-                case DATAOUT:
-                    ret = TelephonyManager.DATA_ACTIVITY_OUT;
-                break;
-
-                case DATAINANDOUT:
-                    ret = TelephonyManager.DATA_ACTIVITY_INOUT;
-                break;
-
-                case DORMANT:
-                    ret = TelephonyManager.DATA_ACTIVITY_DORMANT;
-                break;
-
-                default:
-                    ret = TelephonyManager.DATA_ACTIVITY_NONE;
-                break;
-            }
-        }
-
-        return ret;
+        return getDataNetworkController().getDataActivity();
     }
 
     /**
@@ -1434,8 +1332,7 @@
         // emergency number list on another SIM, but is not on theirs.  In this case we will use the
         // emergency number list for this carrier's SIM only.
         if (useOnlyDialedSimEccList) {
-            isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString,
-                    true /* exactMatch */);
+            isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString);
             logi("dial; isEmergency=" + isEmergency
                     + " (based on this phone only); globalIsEmergency="
                     + tm.isEmergencyNumber(dialString));
@@ -1463,9 +1360,10 @@
                 stripSeparators(dialString));
         boolean isMmiCode = (dialPart.startsWith("*") || dialPart.startsWith("#"))
                 && dialPart.endsWith("#");
-        boolean isSuppServiceCode = ImsPhoneMmiCode.isSuppServiceCodes(dialPart, this);
-        boolean isPotentialUssdCode = isMmiCode && !isSuppServiceCode;
-        boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
+        SsDomainController.SuppServiceRoutingInfo ssInfo =
+                ImsPhoneMmiCode.getSuppServiceRoutingInfo(dialPart, this);
+        boolean isPotentialUssdCode = isMmiCode && (ssInfo == null);
+        boolean useImsForUt = ssInfo != null && ssInfo.useSsOverUt();
         boolean useImsForCall = useImsForCall(dialArgs)
                 && (isWpsCall ? allowWpsOverIms : true);
 
@@ -1476,7 +1374,8 @@
                     + ", useImsForEmergency=" + useImsForEmergency
                     + ", useImsForUt=" + useImsForUt
                     + ", isUt=" + isMmiCode
-                    + ", isSuppServiceCode=" + isSuppServiceCode
+                    + ", isSuppServiceCode=" + (ssInfo != null)
+                    + ", useSsOverUt=" + (ssInfo != null && ssInfo.useSsOverUt())
                     + ", isPotentialUssdCode=" + isPotentialUssdCode
                     + ", isWpsCall=" + isWpsCall
                     + ", allowWpsOverIms=" + allowWpsOverIms
@@ -1491,6 +1390,12 @@
                     + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
         }
 
+        // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN
+        if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) {
+            throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED,
+                    "cannot dial number blocked by FDN");
+        }
+
         // Bypass WiFi Only WFC check if this is an emergency call - we should still try to
         // place over cellular if possible.
         if (!isEmergency) {
@@ -1526,6 +1431,10 @@
             }
         }
 
+        if (ssInfo != null && !ssInfo.supportsCsfb()) {
+            throw new CallStateException("not support csfb for supplementary services");
+        }
+
         if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
                 && mSST.mSS.getDataRegistrationState() != ServiceState.STATE_IN_SERVICE
                 && !isEmergency) {
@@ -1688,6 +1597,13 @@
             return true;
         }
 
+        // Perform FDN check
+        if(FdnUtils.isNumberBlockedByFDN(mPhoneId, ussdRequest, getCountryIso())) {
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
+            return true;
+        }
+
         // Try over IMS if possible.
         Phone imsPhone = mImsPhone;
         if ((imsPhone != null)
@@ -1787,7 +1703,7 @@
     public void setRadioPower(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
-                Phone.RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     @Override
@@ -1797,6 +1713,11 @@
                 forceApply, reason);
     }
 
+    @Override
+    public Set<Integer> getRadioPowerOffReasons() {
+        return mSST.getRadioPowerOffReasons();
+    }
+
     private void storeVoiceMailNumber(String number) {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
@@ -2309,12 +2230,36 @@
         mSsOverCdmaSupported = b.getBoolean(CarrierConfigManager.KEY_SUPPORT_SS_OVER_CDMA_BOOL);
     }
 
+    private void updateSsOverUtConfig(PersistableBundle b) {
+        mSsDomainController.updateSsOverUtConfig(b);
+    }
+
     @Override
-    public boolean useSsOverIms(Message onComplete) {
+    public SsDomainController getSsDomainController() {
+        return mSsDomainController;
+    }
+
+    /** Checks the static configuration for the given Call Barring service. */
+    public boolean useCbOverUt(String facility) {
+        return mSsDomainController.useCbOverUt(facility);
+    }
+
+    /** Checks the static configuration for the given Call Forwarding service. */
+    public boolean useCfOverUt(int reason) {
+        return mSsDomainController.useCfOverUt(reason);
+    }
+
+    /** Checks the static configuration for the given supplementary service. */
+    public boolean useSsOverUt(String service) {
+        return mSsDomainController.useSsOverUt(service);
+    }
+
+    @Override
+    public boolean useSsOverUt(Message onComplete) {
         boolean isUtEnabled = isUtEnabled();
 
-        Rlog.d(LOG_TAG, "useSsOverIms: isUtEnabled()= " + isUtEnabled +
-                " isCsRetry(onComplete))= " + isCsRetry(onComplete));
+        Rlog.d(LOG_TAG, "useSsOverUt: isUtEnabled()= " + isUtEnabled
+                + " isCsRetry(onComplete))= " + isCsRetry(onComplete));
 
         if (isUtEnabled && !isCsRetry(onComplete)) {
             return true;
@@ -2322,6 +2267,58 @@
         return false;
     }
 
+    /**
+     * Checks the availability of Ut directly without SsDomainController.
+     * This is only applicable for the case that the terminal-based service
+     * is handled by the IMS service alone without interworking with framework.
+     */
+    private boolean useTerminalBasedServiceOverIms(Message onComplete) {
+        Phone imsPhone = mImsPhone;
+        if (imsPhone == null) {
+            logd("useTerminalBasedServiceOverIms: called for GsmCdma");
+            return false;
+        }
+
+        boolean isUtEnabled = imsPhone.isUtEnabled();
+        Rlog.d(LOG_TAG, "useTerminalBasedServiceOverIms isUtEnabled= " + isUtEnabled
+                + " isCsRetry(onComplete))= " + isCsRetry(onComplete));
+        return isUtEnabled && !isCsRetry(onComplete);
+    }
+
+    /**
+     * Returns whether CSFB is supported for supplementary services.
+     */
+    public boolean supportCsfbForSs() {
+        return mSsDomainController.supportCsfb();
+    }
+
+    /**
+     * Returns whether the carrier supports the terminal-based call waiting service
+     * and Ims service handles it by itself.
+     */
+    private boolean getOemHandlesTerminalBasedCallWaiting() {
+        return mSsDomainController.getOemHandlesTerminalBasedCallWaiting();
+    }
+
+    /**
+     * Returns whether the carrier supports the terminal-based CLIR
+     * and Ims service handles it by itself.
+     */
+    private boolean getOemHandlesTerminalBasedClir() {
+        return mSsDomainController.getOemHandlesTerminalBasedClir();
+    }
+
+    /**
+     * Sends response indicating no nework is available for supplementary services.
+     */
+    private void responseInvalidState(Message onComplete) {
+        if (onComplete == null) return;
+        AsyncResult.forMessage(onComplete, null,
+                new CommandException(CommandException.Error.INVALID_STATE,
+                        "No network available for supplementary services"));
+        onComplete.sendToTarget();
+    }
+
     @Override
     public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
         getCallForwardingOption(commandInterfaceCFReason,
@@ -2331,12 +2328,27 @@
     @Override
     public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
             Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete);
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useCfOverUt(commandInterfaceCFReason)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.getCallForwardingOption(commandInterfaceCFReason,
+                        serviceClass, onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
                 if (DBG) logd("requesting call forwarding query.");
@@ -2380,13 +2392,28 @@
             int serviceClass,
             int timerSeconds,
             Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason,
-                    dialingNumber, serviceClass, timerSeconds, onComplete);
+        // Perform FDN check
+        SsData.RequestType requestType = GsmMmiCode.cfActionToRequestType(commandInterfaceCFAction);
+        SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+        if(isRequestBlockedByFDN(requestType, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useCfOverUt(commandInterfaceCFReason)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason,
+                        dialingNumber, serviceClass, timerSeconds, onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
                     (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
@@ -2433,12 +2460,26 @@
     @Override
     public void getCallBarring(String facility, String password, Message onComplete,
             int serviceClass) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        if (isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useCbOverUt(facility)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.queryFacilityLock(facility, password, serviceClass, onComplete);
         } else {
@@ -2449,12 +2490,28 @@
     @Override
     public void setCallBarring(String facility, boolean lockState, String password,
             Message onComplete, int serviceClass) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
+        // Perform FDN check
+        SsData.RequestType requestType = lockState ? SsData.RequestType.SS_ACTIVATION :
+                SsData.RequestType.SS_DEACTIVATION;
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        if (isRequestBlockedByFDN(requestType, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useCbOverUt(facility)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.setFacilityLock(facility, lockState, password, serviceClass, onComplete);
         } else {
@@ -2472,6 +2529,18 @@
      */
     public void changeCallBarringPassword(String facility, String oldPwd, String newPwd,
             Message onComplete) {
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd(
+                SsData.RequestType.SS_REGISTRATION,
+                serviceType);
+        if(FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso())) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete);
         } else {
@@ -2481,12 +2550,32 @@
 
     @Override
     public void getOutgoingCallerIdDisplay(Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.getOutgoingCallerIdDisplay(onComplete);
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIR)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useSsOverUt(SS_CLIR)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.getOutgoingCallerIdDisplay(onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        } else if (getOemHandlesTerminalBasedClir()) {
+            // Ims service handles the terminal-based CLIR by itself.
+            // Use legacy implementation. Forward the request to Ims service if Ut is available.
+            if (useTerminalBasedServiceOverIms(onComplete)) {
+                imsPhone.getOutgoingCallerIdDisplay(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.getCLIR(onComplete);
         } else {
@@ -2499,12 +2588,33 @@
 
     @Override
     public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
+        // Perform FDN check
+        SsData.RequestType requestType = GsmMmiCode.clirModeToRequestType(commandInterfaceCLIRMode);
+        if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_CLIR)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useSsOverUt(SS_CLIR)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        } else if (getOemHandlesTerminalBasedClir()) {
+            // Ims service handles the terminal-based CLIR by itself.
+            // Use legacy implementation. Forward the request to Ims service if Ut is available.
+            if (useTerminalBasedServiceOverIms(onComplete)) {
+                imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             // Packing CLIR value in the message. This will be required for
             // SharedPreference caching, if the message comes back as part of
@@ -2521,12 +2631,25 @@
 
     @Override
     public void queryCLIP(Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.queryCLIP(onComplete);
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIP)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        Phone imsPhone = mImsPhone;
+        if (useSsOverUt(SS_CLIP)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.queryCLIP(onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.queryCLIP(onComplete);
         } else {
@@ -2539,12 +2662,34 @@
 
     @Override
     public void getCallWaiting(Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.getCallWaiting(onComplete);
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_WAIT)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        if (mCallWaitingController.getCallWaiting(onComplete)) return;
+
+        Phone imsPhone = mImsPhone;
+        if (useSsOverUt(SS_CW)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.getCallWaiting(onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        } else if (getOemHandlesTerminalBasedCallWaiting()) {
+            // Ims service handles the terminal-based call waiting service by itself.
+            // Use legacy implementation. Forward the request to Ims service if Ut is available.
+            if (useTerminalBasedServiceOverIms(onComplete)) {
+                imsPhone.getCallWaiting(onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
             //class parameter in call waiting interrogation  to network
@@ -2580,12 +2725,36 @@
 
     @Override
     public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
-        Phone imsPhone = mImsPhone;
-        if (useSsOverIms(onComplete)) {
-            imsPhone.setCallWaiting(enable, onComplete);
+        // Perform FDN check
+        SsData.RequestType requestType = enable ? SsData.RequestType.SS_ACTIVATION :
+                SsData.RequestType.SS_DEACTIVATION;
+        if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_WAIT)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
             return;
         }
 
+        if (mCallWaitingController.setCallWaiting(enable, serviceClass, onComplete)) return;
+
+        Phone imsPhone = mImsPhone;
+        if (useSsOverUt(SS_CW)) {
+            if (useSsOverUt(onComplete)) {
+                imsPhone.setCallWaiting(enable, onComplete);
+                return;
+            } else if (!supportCsfbForSs()) {
+                responseInvalidState(onComplete);
+                return;
+            }
+        } else if (getOemHandlesTerminalBasedCallWaiting()) {
+            // Ims service handles the terminal-based call waiting service by itself.
+            // Use legacy implementation. Forward the request to Ims service if Ut is available.
+            if (useTerminalBasedServiceOverIms(onComplete)) {
+                imsPhone.setCallWaiting(enable, onComplete);
+                return;
+            }
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.setCallWaiting(enable, serviceClass, onComplete);
         } else if (mSsOverCdmaSupported) {
@@ -2610,6 +2779,24 @@
     }
 
     @Override
+    public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        return mCallWaitingController.getTerminalBasedCallWaitingState(forCsOnly);
+    }
+
+    @Override
+    public void setTerminalBasedCallWaitingStatus(int state) {
+        if (mImsPhone != null) {
+            mImsPhone.setTerminalBasedCallWaitingStatus(state);
+        }
+    }
+
+    @Override
+    public void setTerminalBasedCallWaitingSupported(boolean supported) {
+        mCallWaitingController.setTerminalBasedCallWaitingSupported(supported);
+        mSsDomainController.setOemHandlesTerminalBasedService(!supported);
+    }
+
+    @Override
     public void getAvailableNetworks(Message response) {
         if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
             Message msg = obtainMessage(EVENT_GET_AVAILABLE_NETWORKS_DONE, response);
@@ -2672,25 +2859,12 @@
 
     @Override
     public boolean getDataRoamingEnabled() {
-        if (isUsingNewDataStack()) {
-            return getDataSettingsManager().isDataRoamingEnabled();
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getDataRoamingEnabled();
-        }
-        return false;
+        return getDataSettingsManager().isDataRoamingEnabled();
     }
 
     @Override
     public void setDataRoamingEnabled(boolean enable) {
-        if (isUsingNewDataStack()) {
-            getDataSettingsManager().setDataRoamingEnabled(enable);
-            return;
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                    .setDataRoamingEnabledByUser(enable);
-        }
+        getDataSettingsManager().setDataRoamingEnabled(enable);
     }
 
     @Override
@@ -2735,21 +2909,12 @@
     }
 
     /**
-     * Whether data is enabled by user. Unlike isDataEnabled, this only
-     * checks user setting stored in {@link android.provider.Settings.Global#MOBILE_DATA}
-     * if not provisioning, or isProvisioningDataEnabled if provisioning.
+     * Whether data is enabled by user.
      */
     @Override
     public boolean isUserDataEnabled() {
-        if (isUsingNewDataStack()) {
-            return getDataSettingsManager().isDataEnabledForReason(
-                    TelephonyManager.DATA_ENABLED_REASON_USER);
-        }
-        if (mDataEnabledSettings.isProvisioning()) {
-            return mDataEnabledSettings.isProvisioningDataEnabled();
-        } else {
-            return mDataEnabledSettings.isUserDataEnabled();
-        }
+        return getDataSettingsManager().isDataEnabledForReason(
+                TelephonyManager.DATA_ENABLED_REASON_USER);
     }
 
     /**
@@ -3068,6 +3233,7 @@
                 updateVoNrSettings(b);
                 updateSsOverCdmaSupported(b);
                 loadAllowedNetworksFromSubscriptionDatabase();
+                updateSsOverUtConfig(b);
                 // Obtain new radio capabilities from the modem, since some are SIM-dependent
                 mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
                 break;
@@ -3283,24 +3449,9 @@
             case EVENT_SET_CARRIER_DATA_ENABLED:
                 ar = (AsyncResult) msg.obj;
                 boolean enabled = (boolean) ar.result;
-                if (isUsingNewDataStack()) {
-                    getDataSettingsManager().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
-                            mContext.getOpPackageName());
-                    return;
-                }
-                mDataEnabledSettings.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
-                        enabled);
-                break;
-            case EVENT_DEVICE_PROVISIONED_CHANGE:
-                if (!isUsingNewDataStack()) {
-                    mDataEnabledSettings.updateProvisionedChanged();
-                }
-                break;
-            case EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE:
-                if (!isUsingNewDataStack()) {
-                    mDataEnabledSettings.updateProvisioningDataEnabled();
-                }
+                getDataSettingsManager().setDataEnabled(
+                        TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
+                        mContext.getOpPackageName());
                 break;
             case EVENT_GET_AVAILABLE_NETWORKS_DONE:
                 ar = (AsyncResult) msg.obj;
@@ -3795,10 +3946,6 @@
 
             // send an Intent
             sendEmergencyCallbackModeChange();
-            // Re-initiate data connection
-            if (!isUsingNewDataStack()) {
-                mDataEnabledSettings.setInternalDataEnabled(true);
-            }
             notifyEmergencyCallRegistrants(false);
         }
         mIsTestingEmergencyCallbackMode = false;
@@ -4229,6 +4376,7 @@
     @Override
     public void setImsRegistrationState(boolean registered) {
         mSST.setImsRegistrationState(registered);
+        mCallWaitingController.setImsRegistrationState(registered);
     }
 
     @Override
@@ -4291,6 +4439,20 @@
                         + ServiceState.rilServiceStateToString(mTelecomVoiceServiceStateOverride)
                         + ")");
         pw.flush();
+
+        try {
+            mSsDomainController.dump(pw);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.flush();
+
+        try {
+            mCallWaitingController.dump(pw);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.flush();
     }
 
     @Override
@@ -4409,7 +4571,7 @@
         if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) {
             return null;
         }
-        return subInfo.getCountryIso().toUpperCase();
+        return subInfo.getCountryIso().toUpperCase(Locale.ROOT);
     }
 
     public void notifyEcbmTimerReset(Boolean flag) {
@@ -4587,13 +4749,7 @@
 
     @Override
     public boolean isUtEnabled() {
-        Phone imsPhone = mImsPhone;
-        if (imsPhone != null) {
-            return imsPhone.isUtEnabled();
-        } else {
-            logd("isUtEnabled: called for GsmCdma");
-            return false;
-        }
+        return mSsDomainController.isUtEnabled();
     }
 
     public String getDtmfToneDelayKey() {
@@ -4745,24 +4901,7 @@
      * @return Currently bound data service package names.
      */
     public @NonNull List<String> getDataServicePackages() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().getDataServicePackages();
-        }
-        List<String> packages = new ArrayList<>();
-        int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
-                AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
-
-        for (int transport : transports) {
-            DcTracker dct = getDcTracker(transport);
-            if (dct != null) {
-                String pkg = dct.getDataServiceManager().getDataServicePackageName();
-                if (!TextUtils.isEmpty(pkg)) {
-                    packages.add(pkg);
-                }
-            }
-        }
-
-        return packages;
+        return getDataNetworkController().getDataServicePackages();
     }
 
     private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(
@@ -4887,4 +5026,16 @@
     public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
         return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2);
     }
+
+    /**
+     * The following function checks if supplementary service request is blocked due to FDN.
+     * @param requestType request type associated with the supplementary service
+     * @param serviceType supplementary service type
+     * @return {@code true} if request is blocked due to FDN.
+     */
+    private boolean isRequestBlockedByFDN(SsData.RequestType requestType,
+            SsData.ServiceType serviceType) {
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(requestType, serviceType);
+        return FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso());
+    }
 }
diff --git a/src/java/com/android/internal/telephony/HalVersion.java b/src/java/com/android/internal/telephony/HalVersion.java
index f83d790..c05111b 100644
--- a/src/java/com/android/internal/telephony/HalVersion.java
+++ b/src/java/com/android/internal/telephony/HalVersion.java
@@ -25,6 +25,9 @@
  */
 public class HalVersion implements Comparable<HalVersion> {
 
+    /** The HAL Version indicating that the version is unsupported */
+    public static final HalVersion UNSUPPORTED = new HalVersion(-2, -2);
+
     /** The HAL Version indicating that the version is unknown or invalid */
     public static final HalVersion UNKNOWN = new HalVersion(-1, -1);
 
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 742cc90..ab62aa4 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -39,6 +39,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -337,7 +338,10 @@
         }
 
         efid = updateEfForIccType(efid);
-        if (DBG) logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid).toUpperCase());
+        if (DBG) {
+            logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid)
+                    .toUpperCase(Locale.ROOT));
+        }
 
         checkThread();
         Request loadRequest = new Request();
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 7a128c0..b7c7e7b 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -37,6 +37,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.List;
+import java.util.Locale;
 
 /**
  * {@hide}
@@ -424,7 +425,7 @@
 
     private MatrixCursor loadFromEf(int efType, int subId) {
         if (DBG) log("loadFromEf: efType=0x" +
-                Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
+                Integer.toHexString(efType).toUpperCase(Locale.ROOT) + ", subscription=" + subId);
 
         List<AdnRecord> adnRecords = null;
         try {
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index d3e6a0d..1a5d5b3 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -457,11 +457,11 @@
      */
     public void sendText(String callingPackage, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean persistMessageForNonDefaultSmsApp, long messageId) {
+            boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
         sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
                 persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                 false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
-                messageId);
+                messageId, skipShortCodeCheck);
     }
 
     /**
@@ -481,6 +481,16 @@
                 SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, 0L /* messageId */);
     }
 
+
+    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+            int validityPeriod, boolean isForVvm, long messageId) {
+        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+                persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod, isForVvm,
+                messageId, false);
+    }
+
     /**
      * Send a text based SMS.
      *
@@ -527,12 +537,13 @@
      *  Any Other values including negative considered as Invalid Validity Period of the message.
      * @param messageId An id that uniquely identifies the message requested to be sent.
      *                 Used for logging and diagnostics purposes. The id may be 0.
+     * @param skipShortCodeCheck Skip check for short code type destination address.
      */
 
     private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
             boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
-            int validityPeriod, boolean isForVvm, long messageId) {
+            int validityPeriod, boolean isForVvm, long messageId, boolean skipShortCodeCheck) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr
                     + " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
@@ -544,7 +555,7 @@
         destAddr = filterDestAddress(destAddr);
         mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
-                priority, expectMore, validityPeriod, isForVvm, messageId);
+                priority, expectMore, validityPeriod, isForVvm, messageId, skipShortCodeCheck);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/ImsIndication.java b/src/java/com/android/internal/telephony/ImsIndication.java
new file mode 100644
index 0000000..80d75ce
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsIndication.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION;
+
+import android.hardware.radio.ims.IRadioImsIndication;
+import android.os.AsyncResult;
+
+/**
+ * Interface declaring unsolicited radio indications for IMS APIs.
+ */
+public class ImsIndication extends IRadioImsIndication.Stub {
+    private final RIL mRil;
+
+    public ImsIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioImsIndication.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioImsIndication.VERSION;
+    }
+
+    /**
+     * Fired by radio when any IMS traffic is not sent to network due to any failure
+     * on cellular networks.
+     *
+     * @param indicationType Type of radio indication.
+     * @param token The token provided by {@link #startImsTraffic}.
+     * @param failureInfo Connection failure information.
+     */
+    public void onConnectionSetupFailure(int indicationType, int token,
+            android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        int[] info = new int[3];
+        info[0] = failureInfo.failureReason;
+        info[1] = failureInfo.causeCode;
+        info[2] = failureInfo.waitTimeMillis;
+
+        Object[] response = new Object[2];
+        response[0] = token;
+        response[1] = info;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CONNECTION_SETUP_FAILURE, response);
+
+        mRil.mConnectionSetupFailureRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
+    /**
+     * Fired by radio when ANBR is received form the network.
+     *
+     * @param indicationType Type of radio indication.
+     * @param qosSessionId QoS session ID is used to identify media stream such as audio or video.
+     * @param imsdirection Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The recommended bit rate for the UE
+     *        for a specific logical channel and a specific direction by the network.
+     */
+    public void notifyAnbr(int indicationType, int qosSessionId, int imsdirection,
+            int bitsPerSecond) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        int[] response = new int[3];
+        response[0] = qosSessionId;
+        response[1] = imsdirection;
+        response[2] = bitsPerSecond;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NOTIFY_ANBR, response);
+
+        mRil.mNotifyAnbrRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+    }
+
+    /**
+     * Requests IMS stack to perform graceful IMS deregistration before radio performing
+     * network detach in the events of SIM remove, refresh or and so on. The radio waits for
+     * the IMS deregistration, which will be notified by telephony via
+     * {@link IRadioIms#updateImsRegistrationInfo()}, or a certain timeout interval to start
+     * the network detach procedure.
+     *
+     * @param indicationType Type of radio indication.
+     * @param reason the reason why the deregistration is triggered.
+     */
+    public void triggerImsDeregistration(int indicationType, int reason) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION, reason);
+
+        int[] response = new int[1];
+        response[0] = reason;
+
+        mRil.mTriggerImsDeregistrationRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ImsResponse.java b/src/java/com/android/internal/telephony/ImsResponse.java
new file mode 100644
index 0000000..574cf35
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsResponse.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+
+import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.ims.IRadioImsResponse;
+
+/**
+ * Interface declaring response functions to solicited radio requests for IMS APIs.
+ */
+public class ImsResponse extends IRadioImsResponse.Stub {
+    private final RIL mRil;
+
+    public ImsResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioImsResponse.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioImsResponse.VERSION;
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void setSrvccCallInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void updateImsRegistrationInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param failureInfo Failure information.
+     */
+    public void startImsTrafficResponse(RadioResponseInfo responseInfo,
+            android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_IMS, responseInfo);
+
+        if (rr != null) {
+            Object[] response = { "", null };
+
+            if (responseInfo.error == RadioError.NONE) {
+                if (failureInfo != null) {
+                    int[] info = new int[3];
+                    info[0] = failureInfo.failureReason;
+                    info[1] = failureInfo.causeCode;
+                    info[2] = failureInfo.waitTimeMillis;
+                    response[1] = info;
+                }
+
+                RadioResponse.sendMessageResponse(rr.mResult, response);
+            }
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void stopImsTrafficResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void triggerEpsFallbackResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void sendAnbrQueryResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void updateImsCallStatusResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 42fe7a7..324959a 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -193,6 +193,7 @@
                         SmsConstants.FORMAT_3GPP2.equals(getFormat()),
                         status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK,
                         reason,
+                        networkReasonCode,
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
@@ -416,6 +417,21 @@
     }
 
     @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+            int validityPeriod, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message,
+                statusReportRequested, smsHeader, priority, validityPeriod, messageRef);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message,
+                statusReportRequested, messageRef);
+    }
+
+    @Override
     protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
         return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly);
     }
@@ -489,6 +505,7 @@
 
     @VisibleForTesting
     public void fallbackToPstn(SmsTracker tracker) {
+        tracker.mMessageRef = nextMessageRef();
         mSmsDispatchersController.sendRetrySms(tracker);
     }
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 8f43dce..a077b2c 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -45,7 +45,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.SQLException;
@@ -56,8 +55,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerWhitelistManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -740,19 +737,6 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
-        // onlyCore indicates if the device is in cryptkeeper
-        boolean onlyCore = false;
-        try {
-            onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))
-                    .isOnlyCoreApps();
-        } catch (RemoteException e) {
-        }
-        if (onlyCore) {
-            // Device is unable to receive SMS in encrypted state
-            log("Received a short message in encrypted state. Rejecting.");
-            return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED;
-        }
-
         int result = dispatchMessageRadioSpecific(smsb, smsSource);
 
         // In case of error, add to metrics. This is not required in case of success, as the
@@ -875,7 +859,6 @@
      * <code>RESULT_SMS_DISPATCH_FAILURE</code><br>
      * <code>RESULT_SMS_NULL_PDU</code><br>
      * <code>RESULT_SMS_NULL_MESSAGE</code><br>
-     * <code>RESULT_SMS_RECEIVED_WHILE_ENCRYPTED</code><br>
      * <code>RESULT_SMS_DATABASE_ERROR</code><br>
      * <code>RESULT_SMS_INVALID_URI</code><br>
      */
@@ -1159,7 +1142,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void showNewMessageNotification() {
         // Do not show the notification on non-FBE devices.
-        if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
+        if (!StorageManager.isFileEncrypted()) {
             return;
         }
         log("Show new message notification.");
diff --git a/src/java/com/android/internal/telephony/MessagingIndication.java b/src/java/com/android/internal/telephony/MessagingIndication.java
index 96e74cc..5814e3d 100644
--- a/src/java/com/android/internal/telephony/MessagingIndication.java
+++ b/src/java/com/android/internal/telephony/MessagingIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CDMA_NEW_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS;
@@ -47,7 +49,7 @@
      */
     public void cdmaNewSms(int indicationType,
             android.hardware.radio.messaging.CdmaSmsMessage msg) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
 
@@ -63,7 +65,7 @@
      * @param indicationType Type of radio indication
      */
     public void cdmaRuimSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
 
@@ -82,7 +84,7 @@
      *        BTS as coded in 3GPP 23.041 Section 9.4.2.2
      */
     public void newBroadcastSms(int indicationType, byte[] data) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogvRet(
@@ -101,7 +103,7 @@
      *        The PDU starts with the SMSC address per TS 27.005 (+CMT:)
      */
     public void newSms(int indicationType, byte[] pdu) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
 
         SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
@@ -117,7 +119,7 @@
      * @param recordNumber Record number on the SIM
      */
     public void newSmsOnSim(int indicationType, int recordNumber) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
 
@@ -133,7 +135,7 @@
      *        The PDU starts with the SMSC address per TS 27.005 (+CMT:)
      */
     public void newSmsStatusReport(int indicationType, byte[] pdu) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
 
@@ -149,7 +151,7 @@
      * @param indicationType Type of radio indication
      */
     public void simSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
 
diff --git a/src/java/com/android/internal/telephony/MessagingResponse.java b/src/java/com/android/internal/telephony/MessagingResponse.java
index 3dc1d1a..19211e1 100644
--- a/src/java/com/android/internal/telephony/MessagingResponse.java
+++ b/src/java/com/android/internal/telephony/MessagingResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.messaging.IRadioMessagingResponse;
@@ -36,7 +38,7 @@
 
     private void responseSms(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.SendSmsResult sms) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             long messageId = RIL.getOutgoingSmsMessageId(rr.mResult);
@@ -62,35 +64,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeIncomingGsmSmsWithPduResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeLastIncomingGsmSmsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deleteSmsOnRuimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deleteSmsOnSimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -99,7 +101,7 @@
      */
     public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configs) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             int[] ret;
@@ -148,7 +150,7 @@
      */
     public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configs) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<>();
@@ -168,14 +170,14 @@
      * @param smsc Short Message Service Center address on the device
      */
     public void getSmscAddressResponse(RadioResponseInfo responseInfo, String smsc) {
-        RadioResponse.responseString(RIL.MESSAGING_SERVICE, mRil, responseInfo, smsc);
+        RadioResponse.responseString(HAL_SERVICE_MESSAGING, mRil, responseInfo, smsc);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void reportSmsMemoryStatusResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -227,35 +229,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaBroadcastActivationResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setGsmBroadcastActivationResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setGsmBroadcastConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSmscAddressResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -263,7 +265,7 @@
      * @param index record index where the CDMA SMS message is stored
      */
     public void writeSmsToRuimResponse(RadioResponseInfo responseInfo, int index) {
-        RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+        RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
     }
 
     /**
@@ -271,7 +273,7 @@
      * @param index record index where the message is stored
      */
     public void writeSmsToSimResponse(RadioResponseInfo responseInfo, int index) {
-        RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+        RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java
index 4266a75..a20e748 100644
--- a/src/java/com/android/internal/telephony/MockModem.java
+++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -16,6 +16,14 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +42,7 @@
     private static final String BIND_IRADIODATA = "android.telephony.mockmodem.iradiodata";
     private static final String BIND_IRADIONETWORK = "android.telephony.mockmodem.iradionetwork";
     private static final String BIND_IRADIOVOICE = "android.telephony.mockmodem.iradiovoice";
+    private static final String BIND_IRADIOIMS = "android.telephony.mockmodem.iradioims";
     private static final String BIND_IRADIOCONFIG = "android.telephony.mockmodem.iradioconfig";
     private static final String PHONE_ID = "phone_id";
 
@@ -42,7 +51,7 @@
     static final int RADIOCONFIG_SERVICE = RIL.MAX_SERVICE_IDX + 1;
 
     static final int BINDER_RETRY_MILLIS = 3 * 100;
-    static final int BINDER_MAX_RETRY = 3;
+    static final int BINDER_MAX_RETRY = 10;
 
     private Context mContext;
     private String mServiceName;
@@ -54,6 +63,7 @@
     private IBinder mDataBinder;
     private IBinder mNetworkBinder;
     private IBinder mVoiceBinder;
+    private IBinder mImsBinder;
     private IBinder mConfigBinder;
     private ServiceConnection mModemServiceConnection;
     private ServiceConnection mSimServiceConnection;
@@ -61,9 +71,11 @@
     private ServiceConnection mDataServiceConnection;
     private ServiceConnection mNetworkServiceConnection;
     private ServiceConnection mVoiceServiceConnection;
+    private ServiceConnection mImsServiceConnection;
     private ServiceConnection mConfigServiceConnection;
 
     private byte mPhoneId;
+    private String mTag;
 
     MockModem(Context context, String serviceName) {
         this(context, serviceName, 0);
@@ -71,6 +83,7 @@
 
     MockModem(Context context, String serviceName, int phoneId) {
         mPhoneId = (byte) phoneId;
+        mTag = TAG + "-" + mPhoneId;
         mContext = context;
         String[] componentInfo = serviceName.split("/", 2);
         mPackageName = componentInfo[0];
@@ -86,20 +99,22 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder binder) {
-            Rlog.d(TAG, "IRadio " + getModuleName(mService) + "  - onServiceConnected");
+            Rlog.d(mTag, "IRadio " + getModuleName(mService) + "  - onServiceConnected");
 
-            if (mService == RIL.MODEM_SERVICE) {
+            if (mService == HAL_SERVICE_MODEM) {
                 mModemBinder = binder;
-            } else if (mService == RIL.SIM_SERVICE) {
+            } else if (mService == HAL_SERVICE_SIM) {
                 mSimBinder = binder;
-            } else if (mService == RIL.MESSAGING_SERVICE) {
+            } else if (mService == HAL_SERVICE_MESSAGING) {
                 mMessagingBinder = binder;
-            } else if (mService == RIL.DATA_SERVICE) {
+            } else if (mService == HAL_SERVICE_DATA) {
                 mDataBinder = binder;
-            } else if (mService == RIL.NETWORK_SERVICE) {
+            } else if (mService == HAL_SERVICE_NETWORK) {
                 mNetworkBinder = binder;
-            } else if (mService == RIL.VOICE_SERVICE) {
+            } else if (mService == HAL_SERVICE_VOICE) {
                 mVoiceBinder = binder;
+            } else if (mService == HAL_SERVICE_IMS) {
+                mImsBinder = binder;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = binder;
             }
@@ -107,20 +122,22 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            Rlog.d(TAG, "IRadio " + getModuleName(mService) + "  - onServiceDisconnected");
+            Rlog.d(mTag, "IRadio " + getModuleName(mService) + "  - onServiceDisconnected");
 
-            if (mService == RIL.MODEM_SERVICE) {
+            if (mService == HAL_SERVICE_MODEM) {
                 mModemBinder = null;
-            } else if (mService == RIL.SIM_SERVICE) {
+            } else if (mService == HAL_SERVICE_SIM) {
                 mSimBinder = null;
-            } else if (mService == RIL.MESSAGING_SERVICE) {
+            } else if (mService == HAL_SERVICE_MESSAGING) {
                 mMessagingBinder = null;
-            } else if (mService == RIL.DATA_SERVICE) {
+            } else if (mService == HAL_SERVICE_DATA) {
                 mDataBinder = null;
-            } else if (mService == RIL.NETWORK_SERVICE) {
+            } else if (mService == HAL_SERVICE_NETWORK) {
                 mNetworkBinder = null;
-            } else if (mService == RIL.VOICE_SERVICE) {
+            } else if (mService == HAL_SERVICE_VOICE) {
                 mVoiceBinder = null;
+            } else if (mService == HAL_SERVICE_IMS) {
+                mImsBinder = null;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = null;
             }
@@ -138,7 +155,7 @@
 
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(mPackageName, mServiceName));
-        intent.setAction(actionName);
+        intent.setAction(actionName + phoneId);
         intent.putExtra(PHONE_ID, phoneId);
 
         status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
@@ -148,18 +165,20 @@
     /** waitForBinder */
     public IBinder getServiceBinder(int service) {
         switch (service) {
-            case RIL.MODEM_SERVICE:
+            case HAL_SERVICE_MODEM:
                 return mModemBinder;
-            case RIL.SIM_SERVICE:
+            case HAL_SERVICE_SIM:
                 return mSimBinder;
-            case RIL.MESSAGING_SERVICE:
+            case HAL_SERVICE_MESSAGING:
                 return mMessagingBinder;
-            case RIL.DATA_SERVICE:
+            case HAL_SERVICE_DATA:
                 return mDataBinder;
-            case RIL.NETWORK_SERVICE:
+            case HAL_SERVICE_NETWORK:
                 return mNetworkBinder;
-            case RIL.VOICE_SERVICE:
+            case HAL_SERVICE_VOICE:
                 return mVoiceBinder;
+            case HAL_SERVICE_IMS:
+                return mImsBinder;
             case RADIOCONFIG_SERVICE:
                 return mConfigBinder;
             default:
@@ -183,95 +202,109 @@
                 boolean status =
                         bindModuleToMockModemService(BIND_IRADIOCONFIG, mConfigServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Config bind fail");
+                    Rlog.d(mTag, "IRadio Config bind fail");
                     mConfigServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Config is bound");
+                Rlog.d(mTag, "IRadio Config is bound");
             }
-        } else if (service == RIL.MODEM_SERVICE) {
+        } else if (service == HAL_SERVICE_MODEM) {
             if (mModemBinder == null) {
-                mModemServiceConnection = new MockModemConnection(RIL.MODEM_SERVICE);
+                mModemServiceConnection = new MockModemConnection(HAL_SERVICE_MODEM);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOMODEM, mModemServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Modem bind fail");
+                    Rlog.d(mTag, "IRadio Modem bind fail");
                     mModemServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Modem is bound");
+                Rlog.d(mTag, "IRadio Modem is bound");
             }
-        } else if (service == RIL.SIM_SERVICE) {
+        } else if (service == HAL_SERVICE_SIM) {
             if (mSimBinder == null) {
-                mSimServiceConnection = new MockModemConnection(RIL.SIM_SERVICE);
+                mSimServiceConnection = new MockModemConnection(HAL_SERVICE_SIM);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOSIM, mSimServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Sim bind fail");
+                    Rlog.d(mTag, "IRadio Sim bind fail");
                     mSimServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Sim is bound");
+                Rlog.d(mTag, "IRadio Sim is bound");
             }
-        } else if (service == RIL.MESSAGING_SERVICE) {
+        } else if (service == HAL_SERVICE_MESSAGING) {
             if (mMessagingBinder == null) {
-                mMessagingServiceConnection = new MockModemConnection(RIL.MESSAGING_SERVICE);
+                mMessagingServiceConnection = new MockModemConnection(HAL_SERVICE_MESSAGING);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOMESSAGING, mMessagingServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Messaging bind fail");
+                    Rlog.d(mTag, "IRadio Messaging bind fail");
                     mMessagingServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Messaging is bound");
+                Rlog.d(mTag, "IRadio Messaging is bound");
             }
-        } else if (service == RIL.DATA_SERVICE) {
+        } else if (service == HAL_SERVICE_DATA) {
             if (mDataBinder == null) {
-                mDataServiceConnection = new MockModemConnection(RIL.DATA_SERVICE);
+                mDataServiceConnection = new MockModemConnection(HAL_SERVICE_DATA);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIODATA, mDataServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Data bind fail");
+                    Rlog.d(mTag, "IRadio Data bind fail");
                     mDataServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Data is bound");
+                Rlog.d(mTag, "IRadio Data is bound");
             }
-        } else if (service == RIL.NETWORK_SERVICE) {
+        } else if (service == HAL_SERVICE_NETWORK) {
             if (mNetworkBinder == null) {
-                mNetworkServiceConnection = new MockModemConnection(RIL.NETWORK_SERVICE);
+                mNetworkServiceConnection = new MockModemConnection(HAL_SERVICE_NETWORK);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIONETWORK, mNetworkServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Network bind fail");
+                    Rlog.d(mTag, "IRadio Network bind fail");
                     mNetworkServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Network is bound");
+                Rlog.d(mTag, "IRadio Network is bound");
             }
-        } else if (service == RIL.VOICE_SERVICE) {
+        } else if (service == HAL_SERVICE_VOICE) {
             if (mVoiceBinder == null) {
-                mVoiceServiceConnection = new MockModemConnection(RIL.VOICE_SERVICE);
+                mVoiceServiceConnection = new MockModemConnection(HAL_SERVICE_VOICE);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOVOICE, mVoiceServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Voice bind fail");
+                    Rlog.d(mTag, "IRadio Voice bind fail");
                     mVoiceServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Voice is bound");
+                Rlog.d(mTag, "IRadio Voice is bound");
+            }
+        } else if (service == HAL_SERVICE_IMS) {
+            if (mImsBinder == null) {
+                mImsServiceConnection = new MockModemConnection(HAL_SERVICE_IMS);
+
+                boolean status =
+                        bindModuleToMockModemService(
+                                mPhoneId, BIND_IRADIOIMS, mImsServiceConnection);
+                if (!status) {
+                    Rlog.d(TAG, "IRadio Ims bind fail");
+                    mImsServiceConnection = null;
+                }
+            } else {
+                Rlog.d(TAG, "IRadio Ims is bound");
             }
         }
     }
@@ -284,49 +317,56 @@
                 mContext.unbindService(mConfigServiceConnection);
                 mConfigServiceConnection = null;
                 mConfigBinder = null;
-                Rlog.d(TAG, "unbind IRadio Config");
+                Rlog.d(mTag, "unbind IRadio Config");
             }
-        } else if (service == RIL.MODEM_SERVICE) {
+        } else if (service == HAL_SERVICE_MODEM) {
             if (mModemServiceConnection != null) {
                 mContext.unbindService(mModemServiceConnection);
                 mModemServiceConnection = null;
                 mModemBinder = null;
-                Rlog.d(TAG, "unbind IRadio Modem");
+                Rlog.d(mTag, "unbind IRadio Modem");
             }
-        } else if (service == RIL.SIM_SERVICE) {
+        } else if (service == HAL_SERVICE_SIM) {
             if (mSimServiceConnection != null) {
                 mContext.unbindService(mSimServiceConnection);
                 mSimServiceConnection = null;
                 mSimBinder = null;
-                Rlog.d(TAG, "unbind IRadio Sim");
+                Rlog.d(mTag, "unbind IRadio Sim");
             }
-        } else if (service == RIL.MESSAGING_SERVICE) {
+        } else if (service == HAL_SERVICE_MESSAGING) {
             if (mMessagingServiceConnection != null) {
                 mContext.unbindService(mMessagingServiceConnection);
                 mMessagingServiceConnection = null;
                 mMessagingBinder = null;
-                Rlog.d(TAG, "unbind IRadio Messaging");
+                Rlog.d(mTag, "unbind IRadio Messaging");
             }
-        } else if (service == RIL.DATA_SERVICE) {
+        } else if (service == HAL_SERVICE_DATA) {
             if (mDataServiceConnection != null) {
                 mContext.unbindService(mDataServiceConnection);
                 mDataServiceConnection = null;
                 mDataBinder = null;
-                Rlog.d(TAG, "unbind IRadio Data");
+                Rlog.d(mTag, "unbind IRadio Data");
             }
-        } else if (service == RIL.NETWORK_SERVICE) {
+        } else if (service == HAL_SERVICE_NETWORK) {
             if (mNetworkServiceConnection != null) {
                 mContext.unbindService(mNetworkServiceConnection);
                 mNetworkServiceConnection = null;
                 mNetworkBinder = null;
-                Rlog.d(TAG, "unbind IRadio Network");
+                Rlog.d(mTag, "unbind IRadio Network");
             }
-        } else if (service == RIL.VOICE_SERVICE) {
+        } else if (service == HAL_SERVICE_VOICE) {
             if (mVoiceServiceConnection != null) {
                 mContext.unbindService(mVoiceServiceConnection);
                 mVoiceServiceConnection = null;
                 mVoiceBinder = null;
-                Rlog.d(TAG, "unbind IRadio Voice");
+                Rlog.d(mTag, "unbind IRadio Voice");
+            }
+        } else if (service == HAL_SERVICE_IMS) {
+            if (mImsServiceConnection != null) {
+                mContext.unbindService(mImsServiceConnection);
+                mImsServiceConnection = null;
+                mImsBinder = null;
+                Rlog.d(TAG, "unbind IRadio Ims");
             }
         }
     }
@@ -337,18 +377,20 @@
 
     private String getModuleName(int service) {
         switch (service) {
-            case RIL.MODEM_SERVICE:
+            case HAL_SERVICE_MODEM:
                 return "modem";
-            case RIL.SIM_SERVICE:
+            case HAL_SERVICE_SIM:
                 return "sim";
-            case RIL.MESSAGING_SERVICE:
+            case HAL_SERVICE_MESSAGING:
                 return "messaging";
-            case RIL.DATA_SERVICE:
+            case HAL_SERVICE_DATA:
                 return "data";
-            case RIL.NETWORK_SERVICE:
+            case HAL_SERVICE_NETWORK:
                 return "network";
-            case RIL.VOICE_SERVICE:
+            case HAL_SERVICE_VOICE:
                 return "voice";
+            case HAL_SERVICE_IMS:
+                return "ims";
             case RADIOCONFIG_SERVICE:
                 return "config";
             default:
diff --git a/src/java/com/android/internal/telephony/ModemIndication.java b/src/java/com/android/internal/telephony/ModemIndication.java
index d05e0f6..0ee40bb 100644
--- a/src/java/com/android/internal/telephony/ModemIndication.java
+++ b/src/java/com/android/internal/telephony/ModemIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
@@ -44,7 +46,7 @@
      */
     public void hardwareConfigChanged(int indicationType,
             android.hardware.radio.modem.HardwareConfig[] configs) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs);
 
@@ -62,7 +64,7 @@
      *        restart" that explains the cause of the modem restart
      */
     public void modemReset(int indicationType, String reason) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
 
@@ -78,7 +80,7 @@
      */
     public void radioCapabilityIndication(int indicationType,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         RadioCapability response = RILUtils.convertHalRadioCapability(radioCapability, mRil);
 
@@ -94,7 +96,7 @@
      * @param radioState Current radio state
      */
     public void radioStateChanged(int indicationType, int radioState) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         int state = RILUtils.convertHalRadioState(radioState);
         if (mRil.isLogOrTrace()) {
@@ -110,7 +112,7 @@
      * @param indicationType Type of radio indication
      */
     public void rilConnected(int indicationType) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
 
diff --git a/src/java/com/android/internal/telephony/ModemResponse.java b/src/java/com/android/internal/telephony/ModemResponse.java
index 6e44ddc..4589e79 100644
--- a/src/java/com/android/internal/telephony/ModemResponse.java
+++ b/src/java/com/android/internal/telephony/ModemResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.modem.IRadioModemResponse;
@@ -51,7 +53,7 @@
      * @param responseInfo Response info struct containing response type, serial number and error.
      */
     public void enableModemResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
@@ -59,7 +61,7 @@
      * @param version String containing version string for log reporting
      */
     public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) {
-        RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, version);
+        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, version);
     }
 
     /**
@@ -72,7 +74,7 @@
     public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
             String imeisv, String esn, String meid) {
         RadioResponse.responseStrings(
-                RIL.MODEM_SERVICE, mRil, responseInfo, imei, imeisv, esn, meid);
+                HAL_SERVICE_MODEM, mRil, responseInfo, imei, imeisv, esn, meid);
     }
 
     /**
@@ -81,7 +83,7 @@
      */
     public void getHardwareConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.HardwareConfig[] config) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             ArrayList<HardwareConfig> ret = RILUtils.convertHalHardwareConfigList(config);
@@ -98,7 +100,7 @@
      */
     public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.ActivityStatsInfo activityInfo) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             ModemActivityInfo ret = null;
@@ -141,7 +143,7 @@
      * @param isEnabled whether the modem stack is enabled.
      */
     public void getModemStackStatusResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -157,7 +159,7 @@
      */
     public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -179,42 +181,42 @@
      * @param result String containing the contents of the NV item
      */
     public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) {
-        RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, result);
+        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, result);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvResetConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvWriteItemResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void requestShutdownResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendDeviceStateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
@@ -223,7 +225,7 @@
      */
     public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -238,7 +240,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error.
      */
     public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
         mRil.mLastRadioPowerResult = responseInfo.error;
         if (responseInfo.error == RadioError.RF_HARDWARE_ISSUE) {
             AnomalyReporter.reportAnomaly(
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 85053de..f3b91df 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -850,14 +850,9 @@
                     && phone.isUserDataEnabled()
                     && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) {
                 log("setting data to false on " + phone.getSubId());
-                if (phone.isUsingNewDataStack()) {
-                    phone.getDataSettingsManager().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_USER, false,
-                            mContext.getOpPackageName());
-                } else {
-                    phone.getDataEnabledSettings().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_USER, false);
-                }
+                phone.getDataSettingsManager().setDataEnabled(
+                        TelephonyManager.DATA_ENABLED_REASON_USER, false,
+                        mContext.getOpPackageName());
             }
         }
     }
@@ -893,13 +888,9 @@
                 // If enable is true and it's not opportunistic subscription, we don't enable it,
                 // as there can't be two
                 if (phone != null) {
-                    if (phone.isUsingNewDataStack()) {
-                        phone.getDataSettingsManager().setDataEnabled(
-                                TelephonyManager.DATA_ENABLED_REASON_USER, enable,
-                                mContext.getOpPackageName());
-                    } else {
-                        phone.getDataEnabledSettings().setUserDataEnabled(enable, false);
-                    }
+                    phone.getDataSettingsManager().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, enable,
+                            mContext.getOpPackageName());
                 }
             } else {
                 // For inactive subscription, directly write into global settings.
@@ -1074,10 +1065,8 @@
         // existing phone instance.
         Phone[] phones = PhoneFactory.getPhones();
         for (int i = mCallbacksCount; i < phones.length; i++) {
-            if (phones[i].isUsingNewDataStack()) {
-                phones[i].getDataSettingsManager().registerCallback(
-                        new DataSettingsControllerCallback(phones[i], this::post));
-            }
+            phones[i].getDataSettingsManager().registerCallback(
+                    new DataSettingsControllerCallback(phones[i], this::post));
         }
         mCallbacksCount = phones.length;
     }
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index c9ebfd5..a19d4df 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_PRL_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
@@ -39,6 +41,7 @@
 import android.telephony.BarringInfo;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhysicalChannelConfig;
@@ -72,7 +75,7 @@
     public void barringInfoChanged(int indicationType,
             android.hardware.radio.network.CellIdentity cellIdentity,
             android.hardware.radio.network.BarringInfo[] barringInfos) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (cellIdentity == null || barringInfos == null) {
             reportAnomaly(UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"),
@@ -93,7 +96,7 @@
      * @param version PRL version after PRL changes
      */
     public void cdmaPrlChanged(int indicationType, int version) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         int[] response = new int[]{version};
 
@@ -109,7 +112,7 @@
      */
     public void cellInfoList(int indicationType,
             android.hardware.radio.network.CellInfo[] records) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList(records);
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
         mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -122,7 +125,7 @@
      */
     public void currentLinkCapacityEstimate(int indicationType,
             android.hardware.radio.network.LinkCapacityEstimate lce) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
 
@@ -140,7 +143,7 @@
      */
     public void currentPhysicalChannelConfigs(int indicationType,
             android.hardware.radio.network.PhysicalChannelConfig[] configs) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         List<PhysicalChannelConfig> response = new ArrayList<>(configs.length);
         try {
             for (android.hardware.radio.network.PhysicalChannelConfig config : configs) {
@@ -191,7 +194,7 @@
      */
     public void currentSignalStrength(int indicationType,
             android.hardware.radio.network.SignalStrength signalStrength) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
 
@@ -209,7 +212,7 @@
      * @param indicationType Type of radio indication
      */
     public void imsNetworkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
 
@@ -223,7 +226,7 @@
      */
     public void networkScanResult(int indicationType,
             android.hardware.radio.network.NetworkScanResult result) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(result.networkInfos);
         NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
@@ -236,7 +239,7 @@
      * @param indicationType Type of radio indication
      */
     public void networkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
 
@@ -256,7 +259,7 @@
      */
     public void nitzTimeReceived(int indicationType, String nitzTime,
         @ElapsedRealtimeLong long receivedTimeMs, long ageMs) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
 
@@ -303,7 +306,7 @@
     public void registrationFailed(int indicationType,
             android.hardware.radio.network.CellIdentity cellIdentity, String chosenPlmn,
             @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity);
         if (ci == null || TextUtils.isEmpty(chosenPlmn)
                 || (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0
@@ -328,7 +331,7 @@
      * @param state Bitmask of restricted state as defined by PhoneRestrictedState
      */
     public void restrictedStateChanged(int indicationType, int state) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
 
@@ -344,7 +347,7 @@
      */
     public void suppSvcNotify(int indicationType,
             android.hardware.radio.network.SuppSvcNotification suppSvcNotification) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         SuppServiceNotification notification = new SuppServiceNotification();
         notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
@@ -368,7 +371,7 @@
      * @param rat Current new voice rat
      */
     public void voiceRadioTechChanged(int indicationType, int rat) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         int[] response = new int[] {rat};
 
@@ -380,6 +383,25 @@
                 new AsyncResult(null, response, null));
     }
 
+    /**
+     * Emergency Scan Results.
+     * @param indicationType Type of radio indication
+     * @param result the result of the Emergency Network Scan
+     */
+    public void emergencyNetworkScanResult(int indicationType,
+            android.hardware.radio.network.EmergencyRegResult result) {
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
+
+        EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(result);
+
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT, response);
+        }
+
+        mRil.mEmergencyNetworkScanRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
     @Override
     public String getInterfaceHash() {
         return IRadioNetworkIndication.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index d9f70fd..d9de8ab 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -16,12 +16,15 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.network.IRadioNetworkResponse;
 import android.os.AsyncResult;
 import android.telephony.BarringInfo;
 import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalStrength;
@@ -57,7 +60,7 @@
             int halRadioAccessFamilyBitmap) {
         int networkTypeBitmask = RILUtils.convertHalNetworkTypeBitMask(halRadioAccessFamilyBitmap);
         mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, networkTypeBitmask);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, networkTypeBitmask);
     }
 
     /**
@@ -65,7 +68,7 @@
      * @param bandModes List of RadioBandMode listing supported modes
      */
     public void getAvailableBandModesResponse(RadioResponseInfo responseInfo, int[] bandModes) {
-        RadioResponse.responseIntArrayList(RIL.NETWORK_SERVICE, mRil, responseInfo,
+        RadioResponse.responseIntArrayList(HAL_SERVICE_NETWORK, mRil, responseInfo,
                 RILUtils.primitiveArrayToArrayList(bandModes));
     }
 
@@ -75,7 +78,7 @@
      */
     public void getAvailableNetworksResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.OperatorInfo[] networkInfos) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<OperatorInfo> ret = new ArrayList<>();
@@ -98,7 +101,7 @@
     public void getBarringInfoResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.CellIdentity cellIdentity,
             android.hardware.radio.network.BarringInfo[] barringInfos) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             BarringInfo bi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity),
@@ -118,7 +121,7 @@
      * @param type CdmaRoamingType defined in types.hal
      */
     public void getCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo, int type) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, type);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, type);
     }
 
     /**
@@ -127,7 +130,7 @@
      */
     public void getCellInfoListResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.CellInfo[] cellInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(cellInfo);
@@ -144,7 +147,7 @@
      */
     public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RegStateResult dataRegResponse) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -161,7 +164,7 @@
      */
     public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo,
             boolean isRegistered, int ratFamily) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, isRegistered ? 1 : 0,
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, isRegistered ? 1 : 0,
                 ratFamily == android.hardware.radio.RadioTechnologyFamily.THREE_GPP
                         ? PhoneConstants.PHONE_TYPE_GSM : PhoneConstants.PHONE_TYPE_CDMA);
     }
@@ -171,7 +174,7 @@
      * @param selection false for automatic selection, true for manual selection
      */
     public void getNetworkSelectionModeResponse(RadioResponseInfo responseInfo, boolean selection) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, selection ? 1 : 0);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, selection ? 1 : 0);
     }
 
     /**
@@ -183,7 +186,7 @@
     public void getOperatorResponse(RadioResponseInfo responseInfo, String longName,
             String shortName, String numeric) {
         RadioResponse.responseStrings(
-                RIL.NETWORK_SERVICE, mRil, responseInfo, longName, shortName, numeric);
+                HAL_SERVICE_NETWORK, mRil, responseInfo, longName, shortName, numeric);
     }
 
     /**
@@ -192,7 +195,7 @@
      */
     public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.SignalStrength signalStrength) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength);
@@ -209,7 +212,7 @@
      */
     public void getSystemSelectionChannelsResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RadioAccessSpecifier[] halSpecifiers) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<RadioAccessSpecifier> specifiers = new ArrayList<>();
@@ -229,7 +232,7 @@
      * @param rat Current voice RAT
      */
     public void getVoiceRadioTechnologyResponse(RadioResponseInfo responseInfo, int rat) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, rat);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, rat);
     }
 
     /**
@@ -238,7 +241,7 @@
      */
     public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RegStateResult voiceRegResponse) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
                 RadioResponse.sendMessageResponse(rr.mResult, voiceRegResponse);
@@ -254,7 +257,7 @@
      */
     public void isNrDualConnectivityEnabledResponse(RadioResponseInfo responseInfo,
             boolean isEnabled) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -270,7 +273,7 @@
      */
     public void pullLceDataResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.LceDataInfo lceInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo);
@@ -285,105 +288,105 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setAllowedNetworkTypesBitmapResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setBandModeResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setBarringPasswordResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setIndicationFilterResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setLocationUpdatesResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNetworkSelectionModeAutomaticResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNetworkSelectionModeManualResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNrDualConnectivityStateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSignalStrengthReportingCriteriaResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSuppServiceNotificationsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial number and error.
      */
     public void setSystemSelectionChannelsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             NetworkScanResult nsr = null;
             if (responseInfo.error == RadioError.NONE) {
@@ -399,7 +402,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void stopNetworkScanResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             NetworkScanResult nsr = null;
             if (responseInfo.error == RadioError.NONE) {
@@ -417,14 +420,14 @@
      */
     public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo,
             int retriesRemaining) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, retriesRemaining);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, retriesRemaining);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setUsageSettingResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
@@ -433,7 +436,45 @@
      */
     public void getUsageSettingResponse(RadioResponseInfo responseInfo,
             /* @TelephonyManager.UsageSetting */ int usageSetting) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, usageSetting);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, usageSetting);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param regState the current registration state of the modem.
+     */
+    public void setEmergencyModeResponse(RadioResponseInfo responseInfo,
+            android.hardware.radio.network.EmergencyRegResult regState) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(regState);
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, response);
+            }
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void triggerEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void exitEmergencyModeResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void cancelEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     @Override
@@ -445,4 +486,12 @@
     public int getInterfaceVersion() {
         return IRadioNetworkResponse.VERSION;
     }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
 }
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index cc2ca64..f846fa9 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,20 +30,16 @@
 import android.telephony.Annotation;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataCallResponse.LinkStatus;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.util.IState;
 import com.android.internal.util.IndentingPrintWriter;
@@ -55,6 +53,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -86,31 +85,35 @@
     /** Stop all timers and go to current state. */
     public static final int EVENT_UPDATE = 0;
     /** Quit after processing all existing messages. */
-    public static final int EVENT_QUIT = 1;
-    private static final int EVENT_DATA_RAT_CHANGED = 2;
-    private static final int EVENT_NR_STATE_CHANGED = 3;
-    private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
-    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
-    private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
-    private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
-    private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
-    private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
-    private static final int EVENT_INITIALIZE = 12;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
-    private static final int EVENT_PCO_DATA_CHANGED = 14;
-    private static final int EVENT_BANDWIDTH_CHANGED = 15;
-    private static final int EVENT_UPDATE_NR_ADVANCED_STATE = 16;
-    private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 17;
+    private static final int EVENT_QUIT = 1;
+    /** Initialize all events. */
+    private static final int EVENT_INITIALIZE = 2;
+    /** Event for service state changed (data rat, bandwidth, NR state, NR frequency, etc). */
+    private static final int EVENT_SERVICE_STATE_CHANGED = 3;
+    /** Event for physical link status changed. */
+    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 4;
+    /** Event for physical channel config indications turned on/off. */
+    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 5;
+    /** Event for carrier configs changed. */
+    private static final int EVENT_CARRIER_CONFIG_CHANGED = 6;
+    /** Event for primary timer expired. If a secondary timer exists, it will begin afterwards. */
+    private static final int EVENT_PRIMARY_TIMER_EXPIRED = 7;
+    /** Event for secondary timer expired. */
+    private static final int EVENT_SECONDARY_TIMER_EXPIRED = 8;
+    /** Event for radio off or unavailable. */
+    private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 9;
+    /** Event for preferred network mode changed. */
+    private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 10;
+    /** Event for physical channel configs changed. */
+    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 11;
+    /** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */
+    private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12;
 
     private static final String[] sEvents = new String[EVENT_DEVICE_IDLE_MODE_CHANGED + 1];
     static {
         sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
         sEvents[EVENT_QUIT] = "EVENT_QUIT";
-        sEvents[EVENT_DATA_RAT_CHANGED] = "EVENT_DATA_RAT_CHANGED";
-        sEvents[EVENT_NR_STATE_CHANGED] = "EVENT_NR_STATE_CHANGED";
-        sEvents[EVENT_NR_FREQUENCY_CHANGED] = "EVENT_NR_FREQUENCY_CHANGED";
+        sEvents[EVENT_SERVICE_STATE_CHANGED] = "EVENT_SERVICE_STATE_CHANGED";
         sEvents[EVENT_PHYSICAL_LINK_STATUS_CHANGED] = "EVENT_PHYSICAL_LINK_STATUS_CHANGED";
         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED] =
                 "EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED";
@@ -121,15 +124,12 @@
         sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
         sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
-        sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED";
-        sEvents[EVENT_BANDWIDTH_CHANGED] = "EVENT_BANDWIDTH_CHANGED";
-        sEvents[EVENT_UPDATE_NR_ADVANCED_STATE] = "EVENT_UPDATE_NR_ADVANCED_STATE";
         sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED";
     }
 
-    private final Phone mPhone;
-    private final DisplayInfoController mDisplayInfoController;
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+    private final @NonNull Phone mPhone;
+    private final @NonNull DisplayInfoController mDisplayInfoController;
+    private final @NonNull BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
@@ -148,19 +148,19 @@
         }
     };
 
-    private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
-    private String mLteEnhancedPattern = "";
-    private int mOverrideNetworkType;
+    private @NonNull Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
+    private @NonNull String mLteEnhancedPattern = "";
+    private @Annotation.OverrideNetworkType int mOverrideNetworkType;
     private boolean mIsPhysicalChannelConfigOn;
     private boolean mIsPrimaryTimerActive;
     private boolean mIsSecondaryTimerActive;
     private boolean mIsTimerResetEnabledForLegacyStateRRCIdle;
     private int mLtePlusThresholdBandwidth;
     private int mNrAdvancedThresholdBandwidth;
-    private int[] mAdditionalNrAdvancedBandsList;
-    private String mPrimaryTimerState;
-    private String mSecondaryTimerState;
-    private String mPreviousState;
+    private @NonNull int[] mAdditionalNrAdvancedBandsList;
+    private @NonNull String mPrimaryTimerState;
+    private @NonNull String mSecondaryTimerState;
+    private @NonNull String mPreviousState;
     private @LinkStatus int mPhysicalLinkStatus;
     private boolean mIsPhysicalChannelConfig16Supported;
     private boolean mIsNrAdvancedAllowedByPco = false;
@@ -169,6 +169,13 @@
     private boolean mEnableNrAdvancedWhileRoaming = true;
     private boolean mIsDeviceIdleMode = false;
 
+    private @Nullable DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
+    private @Nullable DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
+
+    // Cached copies below to prevent race conditions
+    private @NonNull ServiceState mServiceState;
+    private @Nullable List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
     /**
      * NetworkTypeController constructor.
      *
@@ -181,13 +188,21 @@
         mDisplayInfoController = displayInfoController;
         mOverrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         mIsPhysicalChannelConfigOn = true;
+        mPrimaryTimerState = "";
+        mSecondaryTimerState = "";
+        mPreviousState = "";
         addState(mDefaultState);
         addState(mLegacyState, mDefaultState);
         addState(mIdleState, mDefaultState);
         addState(mLteConnectedState, mDefaultState);
         addState(mNrConnectedState, mDefaultState);
+        addState(mNrConnectedAdvancedState, mDefaultState);
         setInitialState(mDefaultState);
         start();
+
+        mServiceState = mPhone.getServiceStateTracker().getServiceState();
+        mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+
         sendMessage(EVENT_INITIALIZE);
     }
 
@@ -200,10 +215,10 @@
     }
 
     /**
-     * @return True if either the primary or secondary 5G hysteresis timer is active,
-     * and false if neither are.
+     * @return {@code true} if either the primary or secondary 5G icon timers are active,
+     * and {@code false} if neither are.
      */
-    public boolean is5GHysteresisActive() {
+    public boolean areAnyTimersActive() {
         return mIsPrimaryTimerActive || mIsSecondaryTimerActive;
     }
 
@@ -214,41 +229,25 @@
                 EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
         mPhone.registerForPhysicalChannelConfig(getHandler(),
                 EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
-                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
-                EVENT_DATA_RAT_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForBandwidthChanged(
-                getHandler(), EVENT_BANDWIDTH_CHANGED, null);
+        mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
+                EVENT_SERVICE_STATE_CHANGED, null);
         mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
                 TelephonyManager.class).isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
-        mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
-                EVENT_NR_STATE_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
-                EVENT_NR_FREQUENCY_CHANGED, null);
         mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(),
                 EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null);
         IntentFilter filter = new IntentFilter();
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null);
-        }
     }
 
     private void unRegisterForAllEvents() {
         mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
         mPhone.unregisterForPreferredNetworkTypeChanged(getHandler());
-        mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
-                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler());
-        mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
-        mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
+        mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
         mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
         mPhone.getContext().unregisterReceiver(mIntentReceiver);
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.mCi.unregisterForPcoData(getHandler());
-        }
     }
 
     private void parseCarrierConfigs() {
@@ -306,38 +305,46 @@
                         CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
                 mNrAdvancedCapablePcoId = b.getInt(
                         CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
-                if (mNrAdvancedCapablePcoId > 0 && mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) {
+                    mNrAdvancedCapableByPcoChangedCallback =
                             new DataNetworkControllerCallback(getHandler()::post) {
-                                @Override
-                                public void onNrAdvancedCapableByPcoChanged(
-                                        boolean nrAdvancedCapable) {
-                                    log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
-                                    mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
-                                    sendMessage(EVENT_UPDATE_NR_ADVANCED_STATE);
-                                }
-                            });
+                        @Override
+                        public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) {
+                            log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
+                            mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    };
+                    mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                            mNrAdvancedCapableByPcoChangedCallback);
+                } else if (mNrAdvancedCapablePcoId == 0
+                        && mNrAdvancedCapableByPcoChangedCallback != null) {
+                    mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+                            mNrAdvancedCapableByPcoChangedCallback);
+                    mNrAdvancedCapableByPcoChangedCallback = null;
                 }
                 mEnableNrAdvancedWhileRoaming = b.getBoolean(
                         CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
                 mIsUsingUserDataForRrcDetection = b.getBoolean(
                         CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
                 if (!mIsPhysicalChannelConfig16Supported || mIsUsingUserDataForRrcDetection) {
-                    if (mPhone.isUsingNewDataStack()) {
-                        mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                    if (mNrPhysicalLinkStatusChangedCallback == null) {
+                        mNrPhysicalLinkStatusChangedCallback =
                                 new DataNetworkControllerCallback(getHandler()::post) {
-                                    @Override
-                                    public void onPhysicalLinkStatusChanged(
-                                            @LinkStatus int status) {
-                                        sendMessage(obtainMessage(
-                                                EVENT_PHYSICAL_LINK_STATUS_CHANGED,
-                                                new AsyncResult(null, status, null)));
-                                    }});
-                    } else {
-                        mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                                .registerForPhysicalLinkStatusChanged(getHandler(),
-                                        EVENT_PHYSICAL_LINK_STATUS_CHANGED);
+                            @Override
+                            public void onPhysicalLinkStatusChanged(
+                                    @LinkStatus int status) {
+                                sendMessage(obtainMessage(
+                                        EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+                                        new AsyncResult(null, status, null)));
+                            }};
+                        mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                                mNrPhysicalLinkStatusChangedCallback);
                     }
+                } else if (mNrPhysicalLinkStatusChangedCallback != null) {
+                    mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+                            mNrPhysicalLinkStatusChangedCallback);
+                    mNrPhysicalLinkStatusChangedCallback = null;
                 }
             }
         }
@@ -349,7 +356,7 @@
         if (!TextUtils.isEmpty(icons)) {
             // Format: "STATE:ICON,STATE2:ICON2"
             for (String pair : icons.trim().split(",")) {
-                String[] kv = (pair.trim().toLowerCase()).split(":");
+                String[] kv = (pair.trim().toLowerCase(Locale.ROOT)).split(":");
                 if (kv.length != 2) {
                     if (DBG) loge("Invalid 5G icon configuration, config = " + pair);
                     continue;
@@ -376,7 +383,7 @@
         if (!TextUtils.isEmpty(timers)) {
             // Format: "FROM_STATE,TO_STATE,DURATION;FROM_STATE_2,TO_STATE_2,DURATION_2"
             for (String triple : timers.trim().split(";")) {
-                String[] kv = (triple.trim().toLowerCase()).split(",");
+                String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
                 if (kv.length != 3) {
                     if (DBG) loge("Invalid 5G icon timer configuration, config = " + triple);
                     continue;
@@ -402,7 +409,7 @@
         if (!TextUtils.isEmpty(secondaryTimers)) {
             // Format: "PRIMARY_STATE,TO_STATE,DURATION;PRIMARY_STATE_2,TO_STATE_2,DURATION_2"
             for (String triple : secondaryTimers.trim().split(";")) {
-                String[] kv = (triple.trim().toLowerCase()).split(",");
+                String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
                 if (kv.length != 3) {
                     if (DBG) {
                         loge("Invalid 5G icon secondary timer configuration, config = " + triple);
@@ -444,7 +451,7 @@
         int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         int dataNetworkType = getDataNetworkType();
         boolean nrNsa = isLte(dataNetworkType)
-                && mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
+                && mServiceState.getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
         boolean nrSa = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR;
 
         // NR display is not accurate when physical channel config notifications are off
@@ -475,7 +482,7 @@
                 keys.add(STATE_CONNECTED_NR_ADVANCED);
             }
         } else {
-            switch (mPhone.getServiceState().getNrState()) {
+            switch (mServiceState.getNrState()) {
                 case NetworkRegistrationInfo.NR_STATE_CONNECTED:
                     if (isNrAdvanced()) {
                         keys.add(STATE_CONNECTED_NR_ADVANCED);
@@ -505,9 +512,8 @@
     private @Annotation.OverrideNetworkType int getLteDisplayType() {
         int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         if ((getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
-                || mPhone.getServiceState().isUsingCarrierAggregation())
-                && (IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
-                        > mLtePlusThresholdBandwidth)) {
+                || mServiceState.isUsingCarrierAggregation())
+                && getBandwidth() > mLtePlusThresholdBandwidth) {
             value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA;
         }
         if (isLteEnhancedAvailable()) {
@@ -521,8 +527,8 @@
             return false;
         }
         Pattern stringPattern = Pattern.compile(mLteEnhancedPattern);
-        for (String opName : new String[] {mPhone.getServiceState().getOperatorAlphaLongRaw(),
-                mPhone.getServiceState().getOperatorAlphaShortRaw()}) {
+        for (String opName : new String[] {mServiceState.getOperatorAlphaLongRaw(),
+                mServiceState.getOperatorAlphaShortRaw()}) {
             if (!TextUtils.isEmpty(opName)) {
                 Matcher matcher = stringPattern.matcher(opName);
                 if (matcher.find()) {
@@ -542,8 +548,6 @@
             if (DBG) log("DefaultState: process " + getEventName(msg.what));
             switch (msg.what) {
                 case EVENT_UPDATE:
-                case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
-                    if (DBG) log("Reset timers since preferred network mode changed.");
                     resetAllTimers();
                     transitionToCurrentState();
                     break;
@@ -555,26 +559,15 @@
                     break;
                 case EVENT_INITIALIZE:
                     // The reason that we do it here is because some of the works below requires
-                    // other modules (e.g. DcTracker, ServiceStateTracker), which is not created
-                    // yet when NetworkTypeController is created.
+                    // other modules (e.g. DataNetworkController, ServiceStateTracker), which is not
+                    // created yet when NetworkTypeController is created.
                     registerForAllEvents();
                     parseCarrierConfigs();
                     break;
-                case EVENT_DATA_RAT_CHANGED:
-                case EVENT_NR_STATE_CHANGED:
-                case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PCO_DATA_CHANGED:
-                case EVENT_UPDATE_NR_ADVANCED_STATE:
-                    // ignored
-                    break;
-                case EVENT_BANDWIDTH_CHANGED:
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                    }
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    transitionToCurrentState();
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
@@ -616,6 +609,20 @@
                     resetAllTimers();
                     transitionTo(mLegacyState);
                     break;
+                case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
+                    if (DBG) log("Reset timers since preferred network mode changed.");
+                    resetAllTimers();
+                    transitionToCurrentState();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                    }
+                    transitionToCurrentState();
+                    break;
                 case EVENT_DEVICE_IDLE_MODE_CHANGED:
                     PowerManager pm = mPhone.getContext().getSystemService(PowerManager.class);
                     mIsDeviceIdleMode = pm.isDeviceIdleMode();
@@ -664,11 +671,19 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("LegacyState: process " + getEventName(msg.what));
             updateTimers();
-            int rat = getDataNetworkType();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
                     if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
-                        transitionTo(mNrConnectedState);
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (isLte(rat) && isNrNotRestricted()) {
                         transitionWithTimerTo(isPhysicalLinkActive()
                                 ? mLteConnectedState : mIdleState);
@@ -681,21 +696,10 @@
                     }
                     mIsNrRestricted = isNrRestricted();
                     break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (isLte(rat) && isNrNotRestricted()) {
-                        transitionWithTimerTo(isPhysicalLinkActive()
-                                ? mLteConnectedState : mIdleState);
-                    } else if (isLte(rat) && isNrRestricted()) {
-                        updateOverrideNetworkType();
-                    }
-                    mIsNrRestricted = isNrRestricted();
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignored
-                    break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                         if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
@@ -703,8 +707,6 @@
                             resetAllTimers();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
@@ -751,52 +753,51 @@
             if (DBG) log("IdleState: process " + getEventName(msg.what));
             updateTimers();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
                     int rat = getDataNetworkType();
-                    if (rat == TelephonyManager.NETWORK_TYPE_NR) {
-                        transitionTo(mNrConnectedState);
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (!isLte(rat) || !isNrNotRestricted()) {
                         transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (!isNrNotRestricted()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignore
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                        if (isNrNotRestricted()) {
-                            // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
-                            if (isPhysicalLinkActive()) {
-                                transitionWithTimerTo(mLteConnectedState);
-                                break;
-                            }
+                    } else {
+                        if (isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mLteConnectedState);
                         } else {
-                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                            sendMessage(EVENT_NR_STATE_CHANGED);
+                            // Update in case of LTE/LTE+ switch
+                            updateOverrideNetworkType();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                        if (isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mLteConnectedState);
+                        } else {
+                            log("Reevaluating state due to link status changed.");
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (isNrNotRestricted()) {
-                        // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
-                        if (isPhysicalLinkActive()) {
-                            transitionWithTimerTo(mLteConnectedState);
-                        }
+                    if (isPhysicalLinkActive()) {
+                        transitionWithTimerTo(mLteConnectedState);
                     } else {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
+                        log("Reevaluating state due to link status changed.");
+                        sendMessage(EVENT_UPDATE);
                     }
                     break;
                 default:
@@ -835,52 +836,52 @@
             if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
             updateTimers();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
                     int rat = getDataNetworkType();
-                    if (rat == TelephonyManager.NETWORK_TYPE_NR) {
-                        transitionTo(mNrConnectedState);
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (!isLte(rat) || !isNrNotRestricted()) {
                         transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (!isNrNotRestricted()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignore
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                        if (isNrNotRestricted()) {
-                            // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
-                            if (!isPhysicalLinkActive()) {
-                                transitionWithTimerTo(mIdleState);
-                                break;
-                            }
+                    } else {
+                        if (!isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mIdleState);
                         } else {
-                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                            sendMessage(EVENT_NR_STATE_CHANGED);
+                            // Update in case of LTE/LTE+ switch
+                            updateOverrideNetworkType();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                        if (!isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mIdleState);
+                        } else {
+                            log("Reevaluating state due to link status changed.");
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (isNrNotRestricted()) {
-                        // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
-                        if (!isPhysicalLinkActive()) {
-                            transitionWithTimerTo(mIdleState);
-                        }
+                    if (!isPhysicalLinkActive()) {
+                        transitionWithTimerTo(mIdleState);
                     } else {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
+                        log("Reevaluating state due to link status changed.");
+                        sendMessage(EVENT_UPDATE);
                     }
                     break;
                 default:
@@ -904,28 +905,33 @@
      * Device is connected to 5G NR as the primary or secondary cell.
      */
     private final class NrConnectedState extends State {
-        private boolean mIsNrAdvanced = false;
-
         @Override
         public void enter() {
-            if (DBG) log("Entering NrConnectedState(" + getName() + ")");
+            if (DBG) log("Entering NrConnectedState");
             updateTimers();
             updateOverrideNetworkType();
             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
-                mIsNrAdvanced = isNrAdvanced();
                 mPreviousState = getName();
             }
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("NrConnectedState(" + getName() + "): process " + getEventName(msg.what));
+            if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
             updateTimers();
-            int rat = getDataNetworkType();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
                     if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
-                        updateOverrideNetworkType();
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            // Same NR connected state
+                        }
                     } else if (isLte(rat) && isNrNotRestricted()) {
                         transitionWithTimerTo(isPhysicalLinkActive()
                                 ? mLteConnectedState : mIdleState);
@@ -933,37 +939,21 @@
                         transitionWithTimerTo(mLegacyState);
                     }
                     break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isLte(rat) && isNrNotRestricted()) {
-                        transitionWithTimerTo(isPhysicalLinkActive()
-                                ? mLteConnectedState : mIdleState);
-                    } else if (rat != TelephonyManager.NETWORK_TYPE_NR && !isNrConnected()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_PCO_DATA_CHANGED:
-                    handlePcoData((AsyncResult) msg.obj);
-                    break;
-                case EVENT_UPDATE_NR_ADVANCED_STATE:
-                    updateNrAdvancedState();
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
-                    updateNrAdvancedState();
+                    // Check NR advanced in case NR advanced bands were added
+                    if (isNrAdvanced()) {
+                        transitionTo(mNrConnectedAdvancedState);
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (!isNrConnected()) {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
-                    }
-                    break;
-                case EVENT_BANDWIDTH_CHANGED:
-                    updateNrAdvancedState();
                     break;
                 default:
                     return NOT_HANDLED;
@@ -976,58 +966,86 @@
 
         @Override
         public String getName() {
-            return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
-        }
-
-        private void updateNrAdvancedState() {
-            if (!isNrConnected() && getDataNetworkType() != TelephonyManager.NETWORK_TYPE_NR) {
-                log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                sendMessage(EVENT_NR_STATE_CHANGED);
-                return;
-            }
-            boolean isNrAdvanced = isNrAdvanced();
-            if (isNrAdvanced != mIsNrAdvanced) {
-                if (!isNrAdvanced) {
-                    if (DBG) log("updateNrAdvancedState: CONNECTED_NR_ADVANCED -> CONNECTED");
-                    transitionWithTimerTo(mNrConnectedState);
-                } else {
-                    if (DBG) log("updateNrAdvancedState: CONNECTED -> CONNECTED_NR_ADVANCED");
-                    transitionTo(mNrConnectedState);
-                }
-            }
-            mIsNrAdvanced = isNrAdvanced();
-        }
-
-        private void handlePcoData(AsyncResult ar) {
-            if (ar.exception != null) {
-                loge("PCO_DATA exception: " + ar.exception);
-                return;
-            }
-            PcoData pcodata = (PcoData) ar.result;
-            if (pcodata == null) {
-                return;
-            }
-            log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata);
-            DcTracker dcTracker = mPhone.getDcTracker(
-                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            DataConnection dc =
-                    dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null;
-            ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null;
-            if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT)
-                    && mNrAdvancedCapablePcoId > 0
-                    && pcodata.pcoId == mNrAdvancedCapablePcoId
-            ) {
-                log("EVENT_PCO_DATA_CHANGED: NR_ADVANCED is allowed by PCO. length:"
-                        + pcodata.contents.length + ",value: " + Arrays.toString(pcodata.contents));
-                mIsNrAdvancedAllowedByPco = pcodata.contents.length > 0
-                        && pcodata.contents[pcodata.contents.length - 1] == 1;
-                updateNrAdvancedState();
-            }
+            return STATE_CONNECTED;
         }
     }
 
     private final NrConnectedState mNrConnectedState = new NrConnectedState();
 
+    /**
+     * Device is connected to 5G NR as the primary cell and the data rate is higher than
+     * the generic 5G data rate.
+     */
+    private final class NrConnectedAdvancedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log("Entering NrConnectedAdvancedState");
+            updateTimers();
+            updateOverrideNetworkType();
+            if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+                mPreviousState = getName();
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what));
+            updateTimers();
+            switch (msg.what) {
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            // Same NR advanced state
+                        } else {
+                            transitionWithTimerTo(mNrConnectedState);
+                        }
+                    } else if (isLte(rat) && isNrNotRestricted()) {
+                        transitionWithTimerTo(isPhysicalLinkActive()
+                                ? mLteConnectedState : mIdleState);
+                    } else {
+                        transitionWithTimerTo(mLegacyState);
+                    }
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                    }
+                    // Check NR advanced in case NR advanced bands were removed
+                    if (!isNrAdvanced()) {
+                        transitionWithTimerTo(mNrConnectedState);
+                    }
+                    break;
+                case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    mPhysicalLinkStatus = (int) ar.result;
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+                mPreviousState = getName();
+            }
+            return HANDLED;
+        }
+
+        @Override
+        public String getName() {
+            return STATE_CONNECTED_NR_ADVANCED;
+        }
+    }
+
+    private final NrConnectedAdvancedState mNrConnectedAdvancedState =
+            new NrConnectedAdvancedState();
+
     private void transitionWithTimerTo(IState destState) {
         String destName = destState.getName();
         if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName);
@@ -1065,22 +1083,23 @@
     private void transitionToCurrentState() {
         int dataRat = getDataNetworkType();
         IState transitionState;
-        if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) {
-            transitionState = mNrConnectedState;
-            mPreviousState = isNrAdvanced() ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
+        if (dataRat == TelephonyManager.NETWORK_TYPE_NR || (isLte(dataRat) && isNrConnected())) {
+            if (isNrAdvanced()) {
+                transitionState = mNrConnectedAdvancedState;
+            } else {
+                transitionState = mNrConnectedState;
+            }
         } else if (isLte(dataRat) && isNrNotRestricted()) {
             if (isPhysicalLinkActive()) {
                 transitionState = mLteConnectedState;
-                mPreviousState = STATE_NOT_RESTRICTED_RRC_CON;
             } else {
                 transitionState = mIdleState;
-                mPreviousState = STATE_NOT_RESTRICTED_RRC_IDLE;
             }
         } else {
             transitionState = mLegacyState;
-            mPreviousState = isNrRestricted() ? STATE_RESTRICTED : STATE_LEGACY;
         }
         if (!transitionState.equals(getCurrentState())) {
+            mPreviousState = getCurrentState().getName();
             transitionTo(transitionState);
         } else {
             updateOverrideNetworkType();
@@ -1229,17 +1248,15 @@
     }
 
     private boolean isNrConnected() {
-        return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
     }
 
     private boolean isNrNotRestricted() {
-        return mPhone.getServiceState().getNrState()
-                == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
     }
 
     private boolean isNrRestricted() {
-        return mPhone.getServiceState().getNrState()
-                == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
     }
 
     /**
@@ -1254,15 +1271,13 @@
 
         // Check if NR advanced is enabled when the device is roaming. Some carriers disable it
         // while the device is roaming.
-        if (mPhone.getServiceState().getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
+        if (mServiceState.getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
             return false;
         }
 
         // Check if meeting minimum bandwidth requirement. For most carriers, there is no minimum
         // bandwidth requirement and mNrAdvancedThresholdBandwidth is 0.
-        if (mNrAdvancedThresholdBandwidth > 0
-                && IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
-                < mNrAdvancedThresholdBandwidth) {
+        if (mNrAdvancedThresholdBandwidth > 0 && getBandwidth() < mNrAdvancedThresholdBandwidth) {
             return false;
         }
 
@@ -1272,18 +1287,15 @@
     }
 
     private boolean isNrMmwave() {
-        return mPhone.getServiceState().getNrFrequencyRange()
-                == ServiceState.FREQUENCY_RANGE_MMWAVE;
+        return mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE;
     }
 
     private boolean isAdditionalNrAdvancedBand() {
-        List<PhysicalChannelConfig> physicalChannelConfigList =
-                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
         if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList)
-                || physicalChannelConfigList == null) {
+                || mPhysicalChannelConfigs == null) {
             return false;
         }
-        for (PhysicalChannelConfig item : physicalChannelConfigList) {
+        for (PhysicalChannelConfig item : mPhysicalChannelConfigs) {
             if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR
                     && ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) {
                 return true;
@@ -1302,19 +1314,21 @@
     }
 
     private int getPhysicalLinkStatusFromPhysicalChannelConfig() {
-        List<PhysicalChannelConfig> physicalChannelConfigList =
-                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
-        return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty())
+        return (mPhysicalChannelConfigs == null || mPhysicalChannelConfigs.isEmpty())
                 ? DataCallResponse.LINK_STATUS_DORMANT : DataCallResponse.LINK_STATUS_ACTIVE;
     }
 
     private int getDataNetworkType() {
-        NetworkRegistrationInfo nri =  mPhone.getServiceState().getNetworkRegistrationInfo(
+        NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
                 : nri.getAccessNetworkTechnology();
     }
 
+    private int getBandwidth() {
+        return IntStream.of(mServiceState.getCellBandwidths()).sum();
+    }
+
     private String getEventName(int event) {
         try {
             return sEvents[event];
diff --git a/src/java/com/android/internal/telephony/NitzSignal.java b/src/java/com/android/internal/telephony/NitzSignal.java
index 889fe95..2619f3d 100644
--- a/src/java/com/android/internal/telephony/NitzSignal.java
+++ b/src/java/com/android/internal/telephony/NitzSignal.java
@@ -19,7 +19,7 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
-import android.os.TimestampedValue;
+import android.app.time.UnixEpochTime;
 
 import java.time.Duration;
 import java.util.Objects;
@@ -88,13 +88,12 @@
     }
 
     /**
-     * Creates a {@link android.os.TimestampedValue} containing the UTC time as the number of
-     * milliseconds since the start of the Unix epoch. The reference time is the time according to
-     * the elapsed realtime clock when that would have been the time, accounting for receipt time
-     * and age.
+     * Creates a {@link UnixEpochTime} containing the UTC time as the number of milliseconds since
+     * the start of the Unix epoch. The reference time is the time according to the elapsed realtime
+     * clock when that would have been the time, accounting for receipt time and age.
      */
-    public TimestampedValue<Long> createTimeSignal() {
-        return new TimestampedValue<>(
+    public UnixEpochTime createTimeSignal() {
+        return new UnixEpochTime(
                 getAgeAdjustedElapsedRealtimeMillis(),
                 getNitzData().getCurrentTimeInMillis());
     }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 4afccd1..3fee2af 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
@@ -40,12 +42,13 @@
 import android.sysprop.TelephonyProperties;
 import android.telecom.VideoProfile;
 import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.SrvccState;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
@@ -60,14 +63,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.Xml;
 
 import com.android.ims.ImsCall;
@@ -80,15 +82,13 @@
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.metrics.SmsStats;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -222,11 +222,8 @@
     // Radio state change
     protected static final int EVENT_RADIO_STATE_CHANGED            = 47;
     protected static final int EVENT_SET_CARRIER_DATA_ENABLED       = 48;
-    protected static final int EVENT_DEVICE_PROVISIONED_CHANGE      = 49;
-    protected static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = 50;
     protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE    = 51;
 
-    private static final int EVENT_ALL_DATA_DISCONNECTED                  = 52;
     protected static final int EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED  = 53;
     protected static final int EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED = 54;
     protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE        = 55;
@@ -303,11 +300,6 @@
     public CommandsInterface mCi;
     protected int mVmCount = 0;
     private boolean mDnsCheckDisabled;
-    // Data connection trackers. For each transport type (e.g. WWAN, WLAN), there will be a
-    // corresponding DcTracker. The WWAN DcTracker is for cellular data connections while
-    // WLAN DcTracker is for IWLAN data connection. For IWLAN legacy mode, only one (WWAN) DcTracker
-    // will be created.
-    protected final SparseArray<DcTracker> mDcTrackers = new SparseArray<>();
     protected DataNetworkController mDataNetworkController;
     /* Used for dispatching signals to configured carrier apps */
     protected CarrierSignalAgent mCarrierSignalAgent;
@@ -340,15 +332,13 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected AtomicReference<UiccCardApplication> mUiccApplication =
             new AtomicReference<UiccCardApplication>();
-    TelephonyTester mTelephonyTester;
+    private TelephonyTester mTelephonyTester;
     private String mName;
     private final String mActionDetached;
     private final String mActionAttached;
     protected DeviceStateMonitor mDeviceStateMonitor;
     protected DisplayInfoController mDisplayInfoController;
-    protected TransportManager mTransportManager;
     protected AccessNetworksManager mAccessNetworksManager;
-    protected DataEnabledSettings mDataEnabledSettings;
     // Used for identify the carrier of current subscription
     protected CarrierResolver mCarrierResolver;
     protected SignalStrengthController mSignalStrengthController;
@@ -371,6 +361,12 @@
     private int mUsageSettingFromModem = SubscriptionManager.USAGE_SETTING_UNKNOWN;
     private boolean mIsUsageSettingSupported = true;
 
+    /**
+     * {@code true} if the new SubscriptionManagerService is enabled, otherwise the old
+     * SubscriptionController is used.
+     */
+    private boolean mIsSubscriptionManagerServiceEnabled = false;
+
     //IMS
     /**
      * {@link CallStateException} message text used to indicate that an IMS call has failed because
@@ -428,8 +424,6 @@
 
     protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList();
 
-    private final RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
-
     private final RegistrantList mCellInfoRegistrants = new RegistrantList();
 
     private final RegistrantList mRedialRegistrants = new RegistrantList();
@@ -474,10 +468,6 @@
 
     protected LinkBandwidthEstimator mLinkBandwidthEstimator;
 
-    /** The flag indicating using the new data stack or not. */
-    // This flag and the old data stack code will be deleted in Android 14.
-    private final boolean mNewDataStackEnabled;
-
     public IccRecords getIccRecords() {
         return mIccRecords.get();
     }
@@ -580,10 +570,6 @@
                 .makeAppSmsManager(context);
         mLocalLog = new LocalLog(64);
 
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            mTelephonyTester = new TelephonyTester(this);
-        }
-
         setUnitTestMode(unitTestMode);
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -619,13 +605,17 @@
         // Initialize SMS stats
         mSmsStats = new SmsStats(this);
 
-        mNewDataStackEnabled = !mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_force_disable_telephony_new_data_stack);
-
         if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
             return;
         }
 
+        if (TelephonyUtils.IS_DEBUGGABLE) {
+            mTelephonyTester = new TelephonyTester(this);
+        }
+
+        mIsSubscriptionManagerServiceEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_using_subscription_manager_service);
+
         // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
         mTelephonyComponentFactory = telephonyComponentFactory;
         mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
@@ -814,11 +804,6 @@
                 break;
             }
 
-            case EVENT_ALL_DATA_DISCONNECTED:
-                if (areAllDataDisconnected()) {
-                    mAllDataDisconnectedRegistrants.notifyRegistrants();
-                }
-                break;
             case EVENT_GET_USAGE_SETTING_DONE:
                 ar = (AsyncResult) msg.obj;
                 if (ar.exception == null) {
@@ -871,7 +856,11 @@
         return null;
     }
 
-    public void notifySrvccState(Call.SrvccState state) {
+    /**
+     * Notifies the change of the SRVCC state.
+     * @param state the new SRVCC state.
+     */
+    public void notifySrvccState(@SrvccState int state) {
     }
 
     public void registerForSilentRedial(Handler h, int what, Object obj) {
@@ -894,6 +883,9 @@
         Call.SrvccState srvccState = Call.SrvccState.NONE;
         if (ret != null && ret.length != 0) {
             int state = ret[0];
+            if (imsPhone != null) {
+                imsPhone.notifySrvccState(state);
+            }
             switch(state) {
                 case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
                     srvccState = Call.SrvccState.STARTED;
@@ -906,11 +898,6 @@
                     break;
                 case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
                     srvccState = Call.SrvccState.COMPLETED;
-                    if (imsPhone != null) {
-                        imsPhone.notifySrvccState(srvccState);
-                    } else {
-                        Rlog.d(LOG_TAG, "HANDOVER_COMPLETED: mImsPhone null");
-                    }
                     break;
                 case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
                 case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
@@ -1932,7 +1919,7 @@
     public boolean isRadioOffForThermalMitigation() {
         ServiceStateTracker sst = getServiceStateTracker();
         return sst != null && sst.getRadioPowerOffReasons()
-                .contains(Phone.RADIO_POWER_REASON_THERMAL);
+                .contains(TelephonyManager.RADIO_POWER_REASON_THERMAL);
     }
 
     /**
@@ -1951,13 +1938,6 @@
     }
 
     /**
-     * @return The instance of transport manager.
-     */
-    public TransportManager getTransportManager() {
-        return null;
-    }
-
-    /**
      * @return The instance of access networks manager.
      */
     public AccessNetworksManager getAccessNetworksManager() {
@@ -2421,7 +2401,7 @@
         try {
             // Format: "REASON=VALUE,REASON2=VALUE2"
             for (String pair : result.trim().split(",")) {
-                String[] networkTypesValues = (pair.trim().toLowerCase()).split("=");
+                String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("=");
                 if (networkTypesValues.length != 2) {
                     Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
                     continue;
@@ -3730,18 +3710,11 @@
      * @return true if there is a matching DUN APN.
      */
     public boolean hasMatchedTetherApnSetting() {
-        if (isUsingNewDataStack()) {
-            NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
-                    NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            if (nrs != null) {
-                return getDataNetworkController().getDataProfileManager()
-                        .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
-            }
-            return false;
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                    .hasMatchedTetherApnSetting();
+        NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (nrs != null) {
+            return getDataNetworkController().getDataProfileManager()
+                    .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
         }
         return false;
     }
@@ -3752,31 +3725,10 @@
      * @return {@code true} if internet data is allowed to be established.
      */
     public boolean isDataAllowed() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().isInternetDataAllowed();
-        }
-        return isDataAllowed(ApnSetting.TYPE_DEFAULT, null);
+        return getDataNetworkController().isInternetDataAllowed();
     }
 
     /**
-     * Report on whether data connectivity is allowed.
-     *
-     * @param apnType APN type
-     * @param reasons The reasons that data can/can't be established. This is an output param.
-     * @return True if data is allowed to be established
-     */
-    public boolean isDataAllowed(@ApnType int apnType, DataConnectionReasons reasons) {
-        if (mAccessNetworksManager != null) {
-            int transport = mAccessNetworksManager.getCurrentTransport(apnType);
-            if (getDcTracker(transport) != null) {
-                return getDcTracker(transport).isDataAllowed(reasons);
-            }
-        }
-        return false;
-    }
-
-
-    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
     public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
@@ -3932,16 +3884,6 @@
         return null;
     }
 
-    /**
-     * Get the current for the default apn DataState. No change notification
-     * exists at this interface -- use
-     * {@link android.telephony.PhoneStateListener} instead.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public PhoneConstants.DataState getDataConnectionState() {
-        return getDataConnectionState(ApnSetting.TYPE_DEFAULT_STRING);
-    }
-
     public void notifyCallForwardingIndicator() {
     }
 
@@ -4621,42 +4563,6 @@
         return false;
     }
 
-    /**
-     * @return True if all data connections are disconnected.
-     */
-    public boolean areAllDataDisconnected() {
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null
-                        && !getDcTracker(transport).areAllDataDisconnected()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    public void registerForAllDataDisconnected(Handler h, int what) {
-        mAllDataDisconnectedRegistrants.addUnique(h, what, null);
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null
-                        && !getDcTracker(transport).areAllDataDisconnected()) {
-                    getDcTracker(transport).registerForAllDataDisconnected(
-                            this, EVENT_ALL_DATA_DISCONNECTED);
-                }
-            }
-        }
-    }
-
-    public void unregisterForAllDataDisconnected(Handler h) {
-        mAllDataDisconnectedRegistrants.remove(h);
-    }
-
-    public DataEnabledSettings getDataEnabledSettings() {
-        return mDataEnabledSettings;
-    }
-
     @UnsupportedAppUsage
     public IccSmsInterfaceManager getIccSmsInterfaceManager(){
         return null;
@@ -4787,16 +4693,6 @@
         return isEmergencyCallOnly;
     }
 
-    /**
-     * Get data connection tracker based on the transport type
-     *
-     * @param transportType Transport type defined in AccessNetworkConstants.TransportType
-     * @return The data connection tracker. Null if not found.
-     */
-    public @Nullable DcTracker getDcTracker(int transportType) {
-        return mDcTrackers.get(transportType);
-    }
-
     // Return true if either CSIM or RUIM app is present. By default it returns false.
     public boolean isCdmaSubscriptionAppPresent() {
         return false;
@@ -4820,10 +4716,24 @@
      * Get the HAL version.
      *
      * @return the current HalVersion
+     *
+     * @deprecated Use {@link #getHalVersion(int service)} instead.
      */
+    @Deprecated
     public HalVersion getHalVersion() {
+        return getHalVersion(HAL_SERVICE_RADIO);
+    }
+
+    /**
+     * Get the HAL version with a specific service.
+     *
+     * @param service the service id to query
+     * @return the current HalVersion for a specific service
+     *
+     */
+    public HalVersion getHalVersion(@HalService int service) {
         if (mCi != null && mCi instanceof RIL) {
-            return ((RIL) mCi).getHalVersion();
+            return ((RIL) mCi).getHalVersion(service);
         }
         return RIL.RADIO_HAL_VERSION_UNKNOWN;
     }
@@ -4865,7 +4775,17 @@
         return null;
     }
 
-    public boolean useSsOverIms(Message onComplete) {
+    /**
+     * Returns the instance of SsDomainController
+     */
+    public SsDomainController getSsDomainController() {
+        return null;
+    }
+
+    /**
+     * Returns whether it will be served with Ut or not.
+     */
+    public boolean useSsOverUt(Message onComplete) {
         return false;
     }
 
@@ -4967,11 +4887,127 @@
     }
 
     /**
-     * @return {@code true} if using the new telephony data stack.
+     * Returns the user's last setting for terminal-based call waiting
+     * @param forCsOnly indicates the caller expects the result for CS calls only
      */
-    // This flag and the old data stack code will be deleted in Android 14.
-    public boolean isUsingNewDataStack() {
-        return mNewDataStackEnabled;
+    public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        return CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+    }
+
+    /**
+     * Notifies the change of the user setting of the terminal-based call waiting service
+     * to IMS service.
+     */
+    public void setTerminalBasedCallWaitingStatus(int state) {
+    }
+
+    /**
+     * Notifies that the IMS service connected supports the terminal-based call waiting service
+     */
+    public void setTerminalBasedCallWaitingSupported(boolean supported) {
+    }
+
+    /**
+     * Triggers the UE initiated EPS fallback procedure.
+     *
+     * @param reason specifies the reason for EPS fallback.
+     * @param response is callback message.
+     */
+    public void triggerEpsFallback(int reason, Message response) {
+        mCi.triggerEpsFallback(reason, response);
+    }
+
+    /**
+     * Sets the emergency mode
+     *
+     * @param emcMode The radio emergency mode type.
+     * @param result Callback message.
+     */
+    public void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+            @Nullable Message result) {
+        mCi.setEmergencyMode(emcMode, result);
+    }
+
+    /**
+     * Triggers an emergency network scan.
+     *
+     * @param accessNetwork Contains the list of access network types to be prioritized
+     *        during emergency scan. The 1st entry has the highest priority.
+     * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+     *        full service scan or any scan.
+     * @param result Callback message.
+     */
+    public void triggerEmergencyNetworkScan(
+            @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+            @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {
+        mCi.triggerEmergencyNetworkScan(accessNetwork, scanType, result);
+    }
+
+    /**
+     * Cancels ongoing emergency network scan
+     * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+     *        If {@code true}, then the modem shall start the new scan from the beginning,
+     *        otherwise the modem shall resume from the last search.
+     * @param result Callback message.
+     */
+    public void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {
+        mCi.cancelEmergencyNetworkScan(resetScan, result);
+    }
+
+    /**
+     * Exits ongoing emergency mode
+     * @param result Callback message.
+     */
+    public void exitEmergencyMode(@Nullable Message result) {
+        mCi.exitEmergencyMode(result);
+    }
+
+    /**
+     * Registers for emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForEmergencyNetworkScan(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mCi.registerForEmergencyNetworkScan(h, what, obj);
+    }
+
+    /**
+     * Unregisters for emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForEmergencyNetworkScan(@NonNull Handler h) {
+        mCi.unregisterForEmergencyNetworkScan(h);
+    }
+
+    /**
+     * Set the UE's ability to accept/reject null ciphered and/or null integrity-protected
+     * connections.
+     *
+     * @param result Callback message.
+     * @param enabled true to allow null ciphered and/or null integrity-protected connections,
+     * false to disallow.
+     */
+    public void setNullCipherAndIntegrityEnabled(@Nullable Message result, boolean enabled) {
+        mCi.setNullCipherAndIntegrityEnabled(result, enabled);
+    }
+
+    /**
+     * @return Telephony tester instance.
+     */
+    public @Nullable TelephonyTester getTelephonyTester() {
+        return mTelephonyTester;
+    }
+
+    /**
+     * @return {@code true} if the new {@link SubscriptionManagerService} is enabled, otherwise the
+     * old {@link SubscriptionController} is used.
+     */
+    public boolean isSubscriptionManagerServiceEnabled() {
+        return mIsSubscriptionManagerServiceEnabled;
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -4996,7 +5032,7 @@
         pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
         pw.println(" getUnitTestMode()=" + getUnitTestMode());
         pw.println(" getState()=" + getState());
-        pw.println(" getIccSerialNumber()=" + getIccSerialNumber());
+        pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber()));
         pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
         pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
         pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
@@ -5009,7 +5045,6 @@
         pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
         pw.println(" isInEmergencySmsMode=" + isInEmergencySmsMode());
         pw.println(" isEcmCanceledForEmergency=" + isEcmCanceledForEmergency());
-        pw.println(" isUsingNewDataStack=" + isUsingNewDataStack());
         pw.println(" service state=" + getServiceState());
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
@@ -5025,16 +5060,6 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null) {
-                    getDcTracker(transport).dump(fd, pw, args);
-                    pw.flush();
-                    pw.println("++++++++++++++++++++++++++++++++");
-                }
-            }
-        }
-
         if (mDataNetworkController != null) {
             try {
                 mDataNetworkController.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 05c325e..ab0b250 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -71,6 +71,7 @@
     private TelephonyManager mTelephonyManager;
     private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+    private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
     /**
      * Init method to instantiate the object
@@ -122,6 +123,21 @@
         }
     }
 
+    // If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2.
+    private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
+            final PhoneCapability staticCapability) {
+        boolean enableVirtualDsda = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_virtual_dsda);
+
+        if (staticCapability.getLogicalModemList().size() > 1 && enableVirtualDsda) {
+            return new PhoneCapability.Builder(staticCapability)
+                    .setMaxActiveVoiceSubscriptions(2)
+                    .build();
+        } else {
+            return staticCapability;
+        }
+    }
+
     /**
      * Static method to get instance.
      */
@@ -180,6 +196,8 @@
                     ar = (AsyncResult) msg.obj;
                     if (ar != null && ar.exception == null) {
                         mStaticCapability = (PhoneCapability) ar.result;
+                        mStaticCapability =
+                                maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
                         notifyCapabilityChanged();
                     } else {
                         log(msg.what + " failure. Not getting phone capability." + ar.exception);
@@ -465,34 +483,46 @@
      * @return true if the modem service is set successfully, false otherwise.
      */
     public boolean setModemService(String serviceName) {
-        if (mRadioConfig == null || mPhones[0] == null) {
-            return false;
-        }
-
         log("setModemService: " + serviceName);
         boolean statusRadioConfig = false;
         boolean statusRil = false;
         final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
+        final boolean isAllowedForBoot =
+                SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
 
-        // Check for ALLOW_MOCK_MODEM_PROPERTY on user builds
-        if (isAllowed || DEBUG) {
-            if (serviceName != null) {
+        // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds
+        if (isAllowed || isAllowedForBoot || DEBUG) {
+            if (mRadioConfig != null) {
                 statusRadioConfig = mRadioConfig.setModemService(serviceName);
-
-                //TODO: consider multi-sim case (b/210073692)
-                statusRil = mPhones[0].mCi.setModemService(serviceName);
-            } else {
-                statusRadioConfig = mRadioConfig.setModemService(null);
-
-                //TODO: consider multi-sim case
-                statusRil = mPhones[0].mCi.setModemService(null);
             }
 
-            return statusRadioConfig && statusRil;
+            if (!statusRadioConfig) {
+                loge("setModemService: switching modem service for radioconfig fail");
+                return false;
+            }
+
+            for (int i = 0; i < getPhoneCount(); i++) {
+                if (mPhones[i] != null) {
+                    statusRil = mPhones[i].mCi.setModemService(serviceName);
+                }
+
+                if (!statusRil) {
+                    loge("setModemService: switch modem for radio " + i + " fail");
+
+                    // Disconnect the switched service
+                    mRadioConfig.setModemService(null);
+                    for (int t = 0; t < i; t++) {
+                        mPhones[t].mCi.setModemService(null);
+                    }
+                    return false;
+                }
+            }
         } else {
             loge("setModemService is not allowed");
             return false;
         }
+
+        return true;
     }
 
      /**
@@ -500,7 +530,6 @@
      * @return the service name of the connected service.
      */
     public String getModemService() {
-        //TODO: consider multi-sim case
         if (mPhones[0] == null) {
             return "";
         }
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 3361b74..e642314 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE;
 
@@ -50,6 +52,7 @@
 import com.android.internal.telephony.imsphone.ImsPhoneFactory;
 import com.android.internal.telephony.metrics.MetricsCollector;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.IndentingPrintWriter;
@@ -83,6 +86,7 @@
     private static IntentBroadcaster sIntentBroadcaster;
     private static @Nullable EuiccController sEuiccController;
     private static @Nullable EuiccCardController sEuiccCardController;
+    private static SubscriptionManagerService sSubscriptionManagerService;
 
     static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
 
@@ -179,7 +183,7 @@
 
                 if (numPhones > 0) {
                     final RadioConfig radioConfig = RadioConfig.make(context,
-                            sCommandsInterfaces[0].getHalVersion());
+                            sCommandsInterfaces[0].getHalVersion(HAL_SERVICE_RADIO));
                     sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig,
                             sCommandsInterfaces[0]);
                 } else {
@@ -194,9 +198,15 @@
                 // call getInstance()
                 sUiccController = UiccController.make(context);
 
-                Rlog.i(LOG_TAG, "Creating SubscriptionController");
-                TelephonyComponentFactory.getInstance().inject(SubscriptionController.class.
-                        getName()).initSubscriptionController(context);
+                if (sContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_using_subscription_manager_service)) {
+                    Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
+                    sSubscriptionManagerService = new SubscriptionManagerService(context);
+                } else {
+                    Rlog.i(LOG_TAG, "Creating SubscriptionController");
+                    TelephonyComponentFactory.getInstance().inject(SubscriptionController.class
+                            .getName()).initSubscriptionController(context);
+                }
                 TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
                         getName()).initMultiSimSettingController(context,
                         SubscriptionController.getInstance());
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 5b4d5e5..f25de35 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -36,9 +35,9 @@
 
 import com.android.internal.telephony.PhoneConstants.DataState;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -211,16 +210,6 @@
     static final String REASON_DATA_UNTHROTTLED = "dataUnthrottled";
     static final String REASON_TRAFFIC_DESCRIPTORS_UPDATED = "trafficDescriptorsUpdated";
 
-    // Reasons for Radio being powered off
-    int RADIO_POWER_REASON_USER = 0;
-    int RADIO_POWER_REASON_THERMAL = 1;
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"RADIO_POWER_REASON_"},
-        value = {
-            RADIO_POWER_REASON_USER,
-            RADIO_POWER_REASON_THERMAL})
-    public @interface RadioPowerReason {}
-
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
     static final int BM_EURO_BAND   = RILConstants.BAND_MODE_EURO;
@@ -593,8 +582,9 @@
      * getServiceState().getState() will not change immediately after this call.
      * registerForServiceStateChanged() to find out when the
      * request is complete. This will set the reason for radio power state as {@link
-     * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
-     * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+     * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+     * requested radio power state will actually be set.
+     * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
      * for details.
      *
      * @param power true means "on", false means "off".
@@ -620,8 +610,9 @@
      * getServiceState().getState() will not change immediately after this call.
      * registerForServiceStateChanged() to find out when the
      * request is complete. This will set the reason for radio power state as {@link
-     * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
-     * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+     * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+     * requested radio power state will actually be set.
+     * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
      * for details.
      *
      * @param power true means "on", false means "off".
@@ -638,7 +629,7 @@
     default void setRadioPower(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
-                RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     /**
@@ -656,11 +647,19 @@
      * @param power true means "on", false means "off".
      * @param reason RadioPowerReason constant defining the reason why the radio power was set.
      */
-    default void setRadioPowerForReason(boolean power, @RadioPowerReason int reason) {
+    default void setRadioPowerForReason(boolean power,
+            @TelephonyManager.RadioPowerReason int reason) {
         setRadioPowerForReason(power, false, false, false, reason);
     }
 
     /**
+     * @return reasons for powering off radio.
+     */
+    default Set<Integer> getRadioPowerOffReasons() {
+        return new HashSet<>();
+    }
+
+    /**
      * Sets the radio power on/off state with option to specify whether it's for emergency call
      * (off is sometimes called "airplane mode") and option to set the reason for setting the power
      * state. Current state can be gotten via {@link #getServiceState()}.
@@ -686,7 +685,7 @@
      */
     default void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply,
-            @RadioPowerReason int reason) {}
+            @TelephonyManager.RadioPowerReason int reason) {}
 
     /**
      * Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index b1dfa5f..02ddf57 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -39,6 +39,7 @@
 import android.util.EventLog;
 
 import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.SIMRecords;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccPort;
 import com.android.telephony.Rlog;
@@ -418,6 +419,29 @@
                 });
     }
 
+    /**
+     * Returns the USIM service table that fetched from EFUST elementary field that are loaded
+     * based on the appType.
+     */
+    public String getSimServiceTable(int subId, int appType) throws RemoteException {
+        return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSimServiceTable",
+                (phone) -> {
+                    UiccPort uiccPort = phone.getUiccPort();
+                    if (uiccPort == null || uiccPort.getUiccProfile() == null) {
+                        loge("getSimServiceTable(): uiccPort or uiccProfile is null");
+                        return null;
+                    }
+                    UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType(
+                            appType);
+                    if (uiccApp == null) {
+                        loge("getSimServiceTable(): no app with specified apptype="
+                                + appType);
+                        return null;
+                    }
+                    return ((SIMRecords)uiccApp.getIccRecords()).getSimServiceTable();
+                });
+    }
+
     @Override
     public String getIccSimChallengeResponse(int subId, int appType, int authType, String data,
             String callingPackage, String callingFeatureId) throws RemoteException {
@@ -438,7 +462,9 @@
             }
 
             if (authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM
-                    && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) {
+                    && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA
+                    && authType != UiccCardApplication.AUTH_CONTEXT_GBA_BOOTSTRAP
+                    && authType != UiccCardApplication.AUTHTYPE_GBA_NAF_KEY_EXTERNAL) {
                 loge("getIccSimChallengeResponse() unsupported authType: " + authType);
                 return null;
             }
@@ -491,7 +517,7 @@
             if (phone != null) {
                 return callMethodHelper.callMethod(phone);
             } else {
-                loge(message + " phone is null for Subscription:" + subId);
+                if (VDBG) loge(message + " phone is null for Subscription:" + subId);
                 return null;
             }
         } finally {
@@ -581,6 +607,30 @@
         }
     }
 
+    /**
+     * Returns SIP URI or tel URI of the Public Service Identity of the SM-SC that fetched from
+     * EFPSISMSC elementary field that are loaded based on the ISIM/USIM appType.
+     */
+    public String getSmscIdentity(int subId, int appType)
+            throws RemoteException {
+        return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSmscIdentity",
+                (phone) -> {
+                    UiccPort uiccPort = phone.getUiccPort();
+                    if (uiccPort == null || uiccPort.getUiccProfile() == null) {
+                        loge("getSmscIdentity(): uiccPort or uiccProfile is null");
+                        return null;
+                    }
+                    UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType(
+                            appType);
+                    if (uiccApp == null) {
+                        loge("getSmscIdentity(): no app with specified type = "
+                                + appType);
+                        return null;
+                    }
+                    return uiccApp.getIccRecords().getSmscIdentity();
+                });
+    }
+
     private void log(String s) {
         Rlog.d(TAG, s);
     }
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 76f0041..498953c 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,7 +28,6 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.telephony.RadioAccessFamily;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -157,34 +156,6 @@
         logd("Constructor - Exit");
     }
 
-    public void registerForAllDataDisconnected(int subId, Handler h, int what) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            mPhones[phoneId].registerForAllDataDisconnected(h, what);
-        }
-    }
-
-    public void unregisterForAllDataDisconnected(int subId, Handler h) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            mPhones[phoneId].unregisterForAllDataDisconnected(h);
-        }
-    }
-
-
-    public boolean areAllDataDisconnected(int subId) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            return mPhones[phoneId].areAllDataDisconnected();
-        } else {
-            // if we can't find a phone for the given subId, it is disconnected.
-            return true;
-        }
-    }
-
     /**
      * Get phone radio type and access technology.
      *
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 6de6652..45dd06c 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import static com.android.internal.telephony.RILConstants.*;
 
 import android.annotation.NonNull;
@@ -45,6 +54,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellInfo;
@@ -55,6 +65,7 @@
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
@@ -67,6 +78,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.TelephonyManager.PrefNetworkMode;
 import android.telephony.data.DataProfile;
 import android.telephony.data.NetworkSliceInfo;
@@ -78,6 +90,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.metrics.ModemRestartStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -92,8 +105,10 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
@@ -133,6 +148,9 @@
     private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
 
     /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_UNSUPPORTED = HalVersion.UNSUPPORTED;
+
+    /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
 
     /** @hide */
@@ -159,8 +177,11 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_2_0 = new HalVersion(2, 0);
 
-    // IRadio version
-    private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_2_1 = new HalVersion(2, 1);
+
+    // Hal version
+    private Map<Integer, HalVersion> mHalVersion = new HashMap<>();
 
     //***** Instance Variables
 
@@ -198,15 +219,9 @@
 
     private static final String PROPERTY_IS_VONR_ENABLED = "persist.radio.is_vonr_enabled_";
 
-    static final int RADIO_SERVICE = 0;
-    static final int DATA_SERVICE = 1;
-    static final int MESSAGING_SERVICE = 2;
-    static final int MODEM_SERVICE = 3;
-    static final int NETWORK_SERVICE = 4;
-    static final int SIM_SERVICE = 5;
-    static final int VOICE_SERVICE = 6;
-    static final int MIN_SERVICE_IDX = RADIO_SERVICE;
-    static final int MAX_SERVICE_IDX = VOICE_SERVICE;
+    public static final int MIN_SERVICE_IDX = HAL_SERVICE_RADIO;
+
+    public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS;
 
     /**
      * An array of sets that records if services are disabled in the HAL for a specific phone ID
@@ -233,6 +248,8 @@
     private volatile IRadio mRadioProxy = null;
     private DataResponse mDataResponse;
     private DataIndication mDataIndication;
+    private ImsResponse mImsResponse;
+    private ImsIndication mImsIndication;
     private MessagingResponse mMessagingResponse;
     private MessagingIndication mMessagingIndication;
     private ModemResponse mModemResponse;
@@ -413,7 +430,8 @@
         public void serviceDied(long cookie) {
             // Deal with service going away
             riljLog("serviceDied");
-            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, RADIO_SERVICE,
+            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                    HAL_SERVICE_RADIO,
                     0 /* ignored arg2 */, cookie));
         }
     }
@@ -428,8 +446,11 @@
 
         public void linkToDeath(IBinder service) throws RemoteException {
             if (service != null) {
+                riljLog("Linked to death for service " + serviceToString(mService));
                 mBinder = service;
                 mBinder.linkToDeath(this, (int) mServiceCookies.get(mService).incrementAndGet());
+            } else {
+                riljLoge("Unable to link to death for service " + serviceToString(mService));
             }
         }
 
@@ -450,7 +471,7 @@
     }
 
     private synchronized void resetProxyAndRequestList(int service) {
-        if (service == RADIO_SERVICE) {
+        if (service == HAL_SERVICE_RADIO) {
             mRadioProxy = null;
         } else {
             mServiceProxies.get(service).clear();
@@ -467,7 +488,7 @@
         // Clear request list on close
         clearRequestList(RADIO_NOT_AVAILABLE, false);
 
-        if (service == RADIO_SERVICE) {
+        if (service == HAL_SERVICE_RADIO) {
             getRadioProxy(null);
         } else {
             getRadioServiceProxy(service, null);
@@ -496,13 +517,13 @@
             // Disable HIDL service
             if (mRadioProxy != null) {
                 riljLog("Disable HIDL service");
-                mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId);
+                mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
             }
 
             mMockModem.bindAllMockModemService();
 
             for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
-                if (service == RADIO_SERVICE) continue;
+                if (service == HAL_SERVICE_RADIO) continue;
 
                 int retryCount = 0;
                 IBinder binder;
@@ -537,15 +558,23 @@
         if ((serviceName == null) || (!serviceBound)) {
             if (serviceBound) riljLog("Unbinding to MockModemService");
 
-            if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) {
-                mDisabledRadioServices.get(RADIO_SERVICE).clear();
+            if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
+                mDisabledRadioServices.get(HAL_SERVICE_RADIO).clear();
             }
 
             if (mMockModem != null) {
-                mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
                 mMockModem = null;
                 for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
-                    resetProxyAndRequestList(service);
+                    if (isRadioServiceSupported(service)) {
+                        resetProxyAndRequestList(service);
+                        if (service == HAL_SERVICE_RADIO && isRadioVersion2_0()) {
+                            mHalVersion.put(service, RADIO_HAL_VERSION_2_0);
+                        } else {
+                            mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+                        }
+                    } else {
+                        mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
+                    }
                 }
             }
         }
@@ -586,7 +615,10 @@
     /** Returns a {@link IRadio} instance or null if the service is not available. */
     @VisibleForTesting
     public synchronized IRadio getRadioProxy(Message result) {
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) return null;
+        if (mHalVersion.containsKey(HAL_SERVICE_RADIO)
+                && mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+            return null;
+        }
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null;
         if (!mIsCellularSupported) {
             if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
@@ -603,14 +635,14 @@
         }
 
         try {
-            if (mDisabledRadioServices.get(RADIO_SERVICE).contains(mPhoneId)) {
+            if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
                 riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                         + " is disabled");
             } else {
                 try {
                     mRadioProxy = android.hardware.radio.V1_6.IRadio.getService(
                             HIDL_SERVICE_NAME[mPhoneId], true);
-                    mRadioVersion = RADIO_HAL_VERSION_1_6;
+                    mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_6);
                 } catch (NoSuchElementException e) {
                 }
 
@@ -618,7 +650,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_5.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_5;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_5);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -627,7 +659,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_4;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_4);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -636,7 +668,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_3;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_3);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -645,7 +677,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_2.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_2;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_2);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -654,7 +686,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_1.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_1;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_1);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -663,7 +695,7 @@
                     try {
                         mRadioProxy = android.hardware.radio.V1_0.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
-                        mRadioVersion = RADIO_HAL_VERSION_1_0;
+                        mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_1_0);
                     } catch (NoSuchElementException e) {
                     }
                 }
@@ -672,11 +704,11 @@
                     if (!mIsRadioProxyInitialized) {
                         mIsRadioProxyInitialized = true;
                         mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
-                                mServiceCookies.get(RADIO_SERVICE).incrementAndGet());
+                                mServiceCookies.get(HAL_SERVICE_RADIO).incrementAndGet());
                         mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
                     }
                 } else {
-                    mDisabledRadioServices.get(RADIO_SERVICE).add(mPhoneId);
+                    mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
                     riljLoge("getRadioProxy: set mRadioProxy for "
                             + HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
                 }
@@ -701,29 +733,32 @@
 
     /**
      * Returns a {@link RadioDataProxy}, {@link RadioMessagingProxy}, {@link RadioModemProxy},
-     * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy}, or an empty {@link RadioServiceProxy}
-     * if the service is not available.
+     * {@link RadioNetworkProxy}, {@link RadioSimProxy}, {@link RadioVoiceProxy},
+     * {@link RadioImsProxy}, or null if the service is not available.
      */
     @NonNull
     public <T extends RadioServiceProxy> T getRadioServiceProxy(Class<T> serviceClass,
             Message result) {
         if (serviceClass == RadioDataProxy.class) {
-            return (T) getRadioServiceProxy(DATA_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_DATA, result);
         }
         if (serviceClass == RadioMessagingProxy.class) {
-            return (T) getRadioServiceProxy(MESSAGING_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_MESSAGING, result);
         }
         if (serviceClass == RadioModemProxy.class) {
-            return (T) getRadioServiceProxy(MODEM_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_MODEM, result);
         }
         if (serviceClass == RadioNetworkProxy.class) {
-            return (T) getRadioServiceProxy(NETWORK_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_NETWORK, result);
         }
         if (serviceClass == RadioSimProxy.class) {
-            return (T) getRadioServiceProxy(SIM_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_SIM, result);
         }
         if (serviceClass == RadioVoiceProxy.class) {
-            return (T) getRadioServiceProxy(VOICE_SERVICE, result);
+            return (T) getRadioServiceProxy(HAL_SERVICE_VOICE, result);
+        }
+        if (serviceClass == RadioImsProxy.class) {
+            return (T) getRadioServiceProxy(HAL_SERVICE_IMS, result);
         }
         riljLoge("getRadioServiceProxy: unrecognized " + serviceClass);
         return null;
@@ -731,12 +766,15 @@
 
     /**
      * Returns a {@link RadioServiceProxy}, which is empty if the service is not available.
-     * For RADIO_SERVICE, use {@link #getRadioProxy} instead, as this will always return null.
+     * For HAL_SERVICE_RADIO, use {@link #getRadioProxy} instead, as this will always return null.
      */
     @VisibleForTesting
     @NonNull
     public synchronized RadioServiceProxy getRadioServiceProxy(int service, Message result) {
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
+        if (service == HAL_SERVICE_IMS && !isRadioServiceSupported(service)) {
+            return mServiceProxies.get(service);
+        }
         if (!mIsCellularSupported) {
             if (RILJ_LOGV) riljLog("getRadioServiceProxy: Not calling getService(): wifi-only");
             if (result != null) {
@@ -753,168 +791,190 @@
         }
 
         try {
-            if (mDisabledRadioServices.get(service).contains(mPhoneId)) {
+            if (mMockModem == null && mDisabledRadioServices.get(service).contains(mPhoneId)) {
                 riljLoge("getRadioServiceProxy: " + serviceToString(service) + " for "
                         + HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
             } else {
                 IBinder binder;
                 switch (service) {
-                    case DATA_SERVICE:
+                    case HAL_SERVICE_DATA:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.data.IRadioData.DESCRIPTOR + "/"
                                             + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(DATA_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_DATA);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioDataProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioDataProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.data.IRadioData.Stub.asInterface(
-                                            binder));
+                                            binder)));
                         }
                         break;
-                    case MESSAGING_SERVICE:
+                    case HAL_SERVICE_MESSAGING:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR
                                             + "/" + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(MESSAGING_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_MESSAGING);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioMessagingProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioMessagingProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.messaging.IRadioMessaging.Stub
-                                            .asInterface(binder));
+                                            .asInterface(binder)));
                         }
                         break;
-                    case MODEM_SERVICE:
+                    case HAL_SERVICE_MODEM:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.modem.IRadioModem.DESCRIPTOR + "/"
                                             + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(MODEM_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_MODEM);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioModemProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioModemProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.modem.IRadioModem.Stub
-                                            .asInterface(binder));
+                                            .asInterface(binder)));
                         }
                         break;
-                    case NETWORK_SERVICE:
+                    case HAL_SERVICE_NETWORK:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.network.IRadioNetwork.DESCRIPTOR + "/"
                                             + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(NETWORK_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_NETWORK);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioNetworkProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioNetworkProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.network.IRadioNetwork.Stub
-                                            .asInterface(binder));
+                                            .asInterface(binder)));
                         }
                         break;
-                    case SIM_SERVICE:
+                    case HAL_SERVICE_SIM:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.sim.IRadioSim.DESCRIPTOR + "/"
                                             + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(SIM_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_SIM);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioSimProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioSimProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.sim.IRadioSim.Stub
-                                            .asInterface(binder));
+                                            .asInterface(binder)));
                         }
                         break;
-                    case VOICE_SERVICE:
+                    case HAL_SERVICE_VOICE:
                         if (mMockModem == null) {
                             binder = ServiceManager.waitForDeclaredService(
                                     android.hardware.radio.voice.IRadioVoice.DESCRIPTOR + "/"
                                             + HIDL_SERVICE_NAME[mPhoneId]);
                         } else {
-                            binder = mMockModem.getServiceBinder(VOICE_SERVICE);
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_VOICE);
                         }
                         if (binder != null) {
-                            mRadioVersion = RADIO_HAL_VERSION_2_0;
-                            ((RadioVoiceProxy) serviceProxy).setAidl(mRadioVersion,
+                            mHalVersion.put(service, ((RadioVoiceProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
                                     android.hardware.radio.voice.IRadioVoice.Stub
-                                            .asInterface(binder));
+                                            .asInterface(binder)));
+                        }
+                        break;
+                    case HAL_SERVICE_IMS:
+                        if (mMockModem == null) {
+                            binder = ServiceManager.waitForDeclaredService(
+                                    android.hardware.radio.ims.IRadioIms.DESCRIPTOR + "/"
+                                            + HIDL_SERVICE_NAME[mPhoneId]);
+                        } else {
+                            binder = mMockModem.getServiceBinder(HAL_SERVICE_IMS);
+                        }
+                        if (binder != null) {
+                            mHalVersion.put(service, ((RadioImsProxy) serviceProxy).setAidl(
+                                    mHalVersion.get(service),
+                                    android.hardware.radio.ims.IRadioIms.Stub
+                                            .asInterface(binder)));
                         }
                         break;
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_6;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_6);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_6.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_5;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_5);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_5.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_4;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_4);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_4.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_3;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_3);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_3.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_2;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_2);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_2.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_1;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_1);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_1.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
                     }
                 }
 
-                if (serviceProxy.isEmpty() && mRadioVersion.less(RADIO_HAL_VERSION_2_0)) {
+                if (serviceProxy.isEmpty()
+                        && mHalVersion.get(service).less(RADIO_HAL_VERSION_2_0)) {
                     try {
-                        mRadioVersion = RADIO_HAL_VERSION_1_0;
-                        serviceProxy.setHidl(mRadioVersion,
+                        mHalVersion.put(service, RADIO_HAL_VERSION_1_0);
+                        serviceProxy.setHidl(mHalVersion.get(service),
                                 android.hardware.radio.V1_0.IRadio.getService(
                                         HIDL_SERVICE_NAME[mPhoneId], true));
                     } catch (NoSuchElementException e) {
@@ -924,45 +984,52 @@
                 if (!serviceProxy.isEmpty()) {
                     if (serviceProxy.isAidl()) {
                         switch (service) {
-                            case DATA_SERVICE:
+                            case HAL_SERVICE_DATA:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioDataProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioDataProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mDataResponse, mDataIndication);
                                 break;
-                            case MESSAGING_SERVICE:
+                            case HAL_SERVICE_MESSAGING:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioMessagingProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioMessagingProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mMessagingResponse, mMessagingIndication);
                                 break;
-                            case MODEM_SERVICE:
+                            case HAL_SERVICE_MODEM:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioModemProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioModemProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mModemResponse, mModemIndication);
                                 break;
-                            case NETWORK_SERVICE:
+                            case HAL_SERVICE_NETWORK:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioNetworkProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioNetworkProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mNetworkResponse, mNetworkIndication);
                                 break;
-                            case SIM_SERVICE:
+                            case HAL_SERVICE_SIM:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioSimProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioSimProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mSimResponse, mSimIndication);
                                 break;
-                            case VOICE_SERVICE:
+                            case HAL_SERVICE_VOICE:
                                 mDeathRecipients.get(service).linkToDeath(
                                         ((RadioVoiceProxy) serviceProxy).getAidl().asBinder());
                                 ((RadioVoiceProxy) serviceProxy).getAidl().setResponseFunctions(
                                         mVoiceResponse, mVoiceIndication);
                                 break;
+                            case HAL_SERVICE_IMS:
+                                mDeathRecipients.get(service).linkToDeath(
+                                        ((RadioImsProxy) serviceProxy).getAidl().asBinder());
+                                ((RadioImsProxy) serviceProxy).getAidl().setResponseFunctions(
+                                        mImsResponse, mImsIndication);
+                                break;
                         }
                     } else {
-                        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+                        if (mHalVersion.get(service)
+                                .greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
                             throw new AssertionError("serviceProxy shouldn't be HIDL with HAL 2.0");
                         }
                         if (!mIsRadioProxyInitialized) {
@@ -975,6 +1042,7 @@
                     }
                 } else {
                     mDisabledRadioServices.get(service).add(mPhoneId);
+                    mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
                     riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
                             + HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
                 }
@@ -1003,7 +1071,7 @@
         for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
             if (active) {
                 // Try to connect to RIL services and set response functions.
-                if (service == RADIO_SERVICE) {
+                if (service == HAL_SERVICE_RADIO) {
                     getRadioProxy(null);
                 } else {
                     getRadioServiceProxy(service, null);
@@ -1044,7 +1112,11 @@
             mRadioBugDetector = new RadioBugDetector(context, mPhoneId);
         }
         try {
-            if (isRadioVersion2_0()) mRadioVersion = RADIO_HAL_VERSION_2_0;
+            if (isRadioVersion2_0()) {
+                mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_2_0);
+            } else {
+                mHalVersion.put(HAL_SERVICE_RADIO, RADIO_HAL_VERSION_UNKNOWN);
+            }
         } catch (SecurityException ex) {
             /* TODO(b/211920208): instead of the following workaround (guessing if we're in a test
              * based on proxies being populated), mock ServiceManager to not throw
@@ -1060,6 +1132,8 @@
         mRadioIndication = new RadioIndication(this);
         mDataResponse = new DataResponse(this);
         mDataIndication = new DataIndication(this);
+        mImsResponse = new ImsResponse(this);
+        mImsIndication = new ImsIndication(this);
         mMessagingResponse = new MessagingResponse(this);
         mMessagingIndication = new MessagingIndication(this);
         mModemResponse = new ModemResponse(this);
@@ -1073,19 +1147,33 @@
         mRilHandler = new RilHandler();
         mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
         for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
-            if (service != RADIO_SERVICE) {
+            if (service != HAL_SERVICE_RADIO) {
+                try {
+                    if (isRadioServiceSupported(service)) {
+                        mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+                    } else {
+                        mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
+                    }
+                } catch (SecurityException ex) {
+                    /* TODO(b/211920208): instead of the following workaround (guessing if
+                    * we're in a test based on proxies being populated), mock ServiceManager
+                    * to not throw SecurityException and return correct value based on what
+                    * HAL we're testing. */
+                    if (proxies == null) throw ex;
+                }
                 mDeathRecipients.put(service, new BinderServiceDeathRecipient(service));
             }
             mDisabledRadioServices.put(service, new HashSet<>());
             mServiceCookies.put(service, new AtomicLong(0));
         }
         if (proxies == null) {
-            mServiceProxies.put(DATA_SERVICE, new RadioDataProxy());
-            mServiceProxies.put(MESSAGING_SERVICE, new RadioMessagingProxy());
-            mServiceProxies.put(MODEM_SERVICE, new RadioModemProxy());
-            mServiceProxies.put(NETWORK_SERVICE, new RadioNetworkProxy());
-            mServiceProxies.put(SIM_SERVICE, new RadioSimProxy());
-            mServiceProxies.put(VOICE_SERVICE, new RadioVoiceProxy());
+            mServiceProxies.put(HAL_SERVICE_DATA, new RadioDataProxy());
+            mServiceProxies.put(HAL_SERVICE_MESSAGING, new RadioMessagingProxy());
+            mServiceProxies.put(HAL_SERVICE_MODEM, new RadioModemProxy());
+            mServiceProxies.put(HAL_SERVICE_NETWORK, new RadioNetworkProxy());
+            mServiceProxies.put(HAL_SERVICE_SIM, new RadioSimProxy());
+            mServiceProxies.put(HAL_SERVICE_VOICE, new RadioVoiceProxy());
+            mServiceProxies.put(HAL_SERVICE_IMS, new RadioImsProxy());
         } else {
             mServiceProxies = proxies;
         }
@@ -1114,7 +1202,7 @@
         // Set radio callback; needed to set RadioIndication callback (should be done after
         // wakelock stuff is initialized above as callbacks are received on separate binder threads)
         for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
-            if (service == RADIO_SERVICE) {
+            if (service == HAL_SERVICE_RADIO) {
                 getRadioProxy(null);
             } else {
                 if (proxies == null) {
@@ -1122,30 +1210,62 @@
                     getRadioServiceProxy(service, null);
                 }
             }
-        }
 
-        if (RILJ_LOGD) {
-            riljLog("Radio HAL version: " + mRadioVersion);
+            if (RILJ_LOGD) {
+                riljLog("HAL version of " + serviceToString(service)
+                        + ": " + mHalVersion.get(service));
+            }
         }
     }
 
     private boolean isRadioVersion2_0() {
-        final String[] serviceNames = new String[] {
-            android.hardware.radio.data.IRadioData.DESCRIPTOR,
-            android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR,
-            android.hardware.radio.modem.IRadioModem.DESCRIPTOR,
-            android.hardware.radio.network.IRadioNetwork.DESCRIPTOR,
-            android.hardware.radio.sim.IRadioSim.DESCRIPTOR,
-            android.hardware.radio.voice.IRadioVoice.DESCRIPTOR,
-        };
-        for (String serviceName : serviceNames) {
-            if (ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) {
+        for (int service = HAL_SERVICE_DATA; service <= MAX_SERVICE_IDX; service++) {
+            if (isRadioServiceSupported(service)) {
                 return true;
             }
         }
         return false;
     }
 
+    private boolean isRadioServiceSupported(int service) {
+        String serviceName = "";
+
+        if (service == HAL_SERVICE_RADIO) {
+            return true;
+        }
+
+        switch (service) {
+            case HAL_SERVICE_DATA:
+                serviceName = android.hardware.radio.data.IRadioData.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_MESSAGING:
+                serviceName = android.hardware.radio.messaging.IRadioMessaging.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_MODEM:
+                serviceName = android.hardware.radio.modem.IRadioModem.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_NETWORK:
+                serviceName = android.hardware.radio.network.IRadioNetwork.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_SIM:
+                serviceName = android.hardware.radio.sim.IRadioSim.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_VOICE:
+                serviceName = android.hardware.radio.voice.IRadioVoice.DESCRIPTOR;
+                break;
+            case HAL_SERVICE_IMS:
+                serviceName = android.hardware.radio.ims.IRadioIms.DESCRIPTOR;
+                break;
+        }
+
+        if (!serviceName.equals("")
+                && ServiceManager.isDeclared(serviceName + '/' + HIDL_SERVICE_NAME[mPhoneId])) {
+            return true;
+        }
+
+        return false;
+    }
+
     private boolean isRadioBugDetectionEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ENABLE_RADIO_BUG_DETECTION, 1) != 0;
@@ -1205,7 +1325,7 @@
             try {
                 simProxy.getIccCardStatus(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getIccCardStatus", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getIccCardStatus", e);
             }
         }
     }
@@ -1252,7 +1372,7 @@
                 simProxy.supplyIccPinForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPinForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPinForApp", e);
             }
         }
     }
@@ -1279,7 +1399,7 @@
                         RILUtils.convertNullToEmptyString(newPin),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPukForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPukForApp", e);
             }
         }
     }
@@ -1305,7 +1425,7 @@
                 simProxy.supplyIccPin2ForApp(rr.mSerial, RILUtils.convertNullToEmptyString(pin),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPin2ForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPin2ForApp", e);
             }
         }
     }
@@ -1332,7 +1452,7 @@
                         RILUtils.convertNullToEmptyString(newPin2),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "supplyIccPuk2ForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplyIccPuk2ForApp", e);
             }
         }
     }
@@ -1360,7 +1480,7 @@
                         RILUtils.convertNullToEmptyString(newPin),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPinForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPinForApp", e);
             }
         }
     }
@@ -1388,7 +1508,7 @@
                         RILUtils.convertNullToEmptyString(newPin2),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "changeIccPin2ForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "changeIccPin2ForApp", e);
             }
         }
     }
@@ -1410,7 +1530,7 @@
                         RILUtils.convertNullToEmptyString(netpin));
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(
-                        NETWORK_SERVICE, "supplyNetworkDepersonalization", e);
+                        HAL_SERVICE_NETWORK, "supplyNetworkDepersonalization", e);
             }
         }
     }
@@ -1420,7 +1540,7 @@
             Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, result,
                     mRILDefaultWorkSource);
 
@@ -1433,7 +1553,7 @@
                 simProxy.supplySimDepersonalization(rr.mSerial, persoType,
                         RILUtils.convertNullToEmptyString(controlKey));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "supplySimDepersonalization", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "supplySimDepersonalization", e);
             }
         } else {
             if (PersoSubState.PERSOSUBSTATE_SIM_NETWORK == persoType) {
@@ -1465,7 +1585,7 @@
             try {
                 voiceProxy.getCurrentCalls(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCurrentCalls", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCurrentCalls", e);
             }
         }
     }
@@ -1481,7 +1601,7 @@
     public void enableModem(boolean enable, Message result) {
         RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
         if (modemProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+        if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_MODEM, result, mRILDefaultWorkSource);
 
             if (RILJ_LOGD) {
@@ -1492,7 +1612,8 @@
             try {
                 modemProxy.enableModem(rr.mSerial, enable);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "enableModem", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM,
+                        "enableModem", e);
             }
         } else {
             if (RILJ_LOGV) riljLog("enableModem: not supported.");
@@ -1509,7 +1630,7 @@
             Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, result,
                     mRILDefaultWorkSource);
 
@@ -1521,7 +1642,8 @@
             try {
                 networkProxy.setSystemSelectionChannels(rr.mSerial, specifiers);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSystemSelectionChannels", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "setSystemSelectionChannels", e);
             }
         } else {
             if (RILJ_LOGV) riljLog("setSystemSelectionChannels: not supported.");
@@ -1537,7 +1659,7 @@
     public void getSystemSelectionChannels(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, result,
                     mRILDefaultWorkSource);
 
@@ -1549,7 +1671,8 @@
             try {
                 networkProxy.getSystemSelectionChannels(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSystemSelectionChannels", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "getSystemSelectionChannels", e);
             }
         } else {
             if (RILJ_LOGV) riljLog("getSystemSelectionChannels: not supported.");
@@ -1565,7 +1688,7 @@
     public void getModemStatus(Message result) {
         RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class, result);
         if (modemProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
+        if (mHalVersion.get(HAL_SERVICE_MODEM).greaterOrEqual(RADIO_HAL_VERSION_1_3)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_MODEM_STATUS, result,
                     mRILDefaultWorkSource);
 
@@ -1576,7 +1699,7 @@
             try {
                 modemProxy.getModemStackStatus(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemStatus", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemStatus", e);
             }
         } else {
             if (RILJ_LOGV) riljLog("getModemStatus: not supported.");
@@ -1591,7 +1714,8 @@
     @Override
     public void dial(String address, boolean isEmergencyCall, EmergencyNumber emergencyNumberInfo,
             boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
-        if (isEmergencyCall && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)
+        if (isEmergencyCall
+                && mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)
                 && emergencyNumberInfo != null) {
             emergencyDial(address, emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode,
                     uusInfo, result);
@@ -1609,7 +1733,7 @@
             try {
                 voiceProxy.dial(rr.mSerial, address, clirMode, uusInfo);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "dial", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "dial", e);
             }
         }
     }
@@ -1618,7 +1742,7 @@
             boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
         RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
         if (voiceProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result,
                     mRILDefaultWorkSource);
 
@@ -1631,7 +1755,7 @@
                 voiceProxy.emergencyDial(rr.mSerial, RILUtils.convertNullToEmptyString(address),
                         emergencyNumberInfo, hasKnownUserIntentEmergency, clirMode, uusInfo);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "emergencyDial", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "emergencyDial", e);
             }
         } else {
             riljLoge("emergencyDial is not supported with 1.4 below IRadio");
@@ -1656,7 +1780,7 @@
             try {
                 simProxy.getImsiForApp(rr.mSerial, RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getImsiForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getImsiForApp", e);
             }
         }
     }
@@ -1675,7 +1799,7 @@
             try {
                 voiceProxy.hangup(rr.mSerial, gsmIndex);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangup", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangup", e);
             }
         }
     }
@@ -1695,7 +1819,7 @@
             try {
                 voiceProxy.hangupWaitingOrBackground(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "hangupWaitingOrBackground", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "hangupWaitingOrBackground", e);
             }
         }
     }
@@ -1716,7 +1840,7 @@
                 voiceProxy.hangupForegroundResumeBackground(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(
-                        VOICE_SERVICE, "hangupForegroundResumeBackground", e);
+                        HAL_SERVICE_VOICE, "hangupForegroundResumeBackground", e);
             }
         }
     }
@@ -1735,7 +1859,8 @@
             try {
                 voiceProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "switchWaitingOrHoldingAndActive", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE,
+                        "switchWaitingOrHoldingAndActive", e);
             }
         }
     }
@@ -1753,7 +1878,7 @@
             try {
                 voiceProxy.conference(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "conference", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "conference", e);
             }
         }
     }
@@ -1771,7 +1896,7 @@
             try {
                 voiceProxy.rejectCall(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "rejectCall", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "rejectCall", e);
             }
         }
     }
@@ -1790,7 +1915,7 @@
             try {
                 voiceProxy.getLastCallFailCause(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getLastCallFailCause", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getLastCallFailCause", e);
             }
         }
     }
@@ -1809,7 +1934,7 @@
             try {
                 networkProxy.getSignalStrength(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getSignalStrength", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getSignalStrength", e);
             }
         }
     }
@@ -1833,7 +1958,7 @@
             try {
                 networkProxy.getVoiceRegistrationState(rr.mSerial, overrideHalVersion);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRegistrationState", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRegistrationState", e);
             }
         }
     }
@@ -1857,7 +1982,7 @@
             try {
                 networkProxy.getDataRegistrationState(rr.mSerial, overrideHalVersion);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getDataRegistrationState", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getDataRegistrationState", e);
             }
         }
     }
@@ -1875,7 +2000,7 @@
             try {
                 networkProxy.getOperator(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getOperator", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getOperator", e);
             }
         }
     }
@@ -1898,7 +2023,7 @@
                 modemProxy.setRadioPower(rr.mSerial, on, forEmergencyCall,
                         preferredForEmergencyCall);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioPower", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioPower", e);
             }
         }
     }
@@ -1917,7 +2042,7 @@
             try {
                 voiceProxy.sendDtmf(rr.mSerial, c + "");
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendDtmf", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendDtmf", e);
             }
         }
     }
@@ -1939,7 +2064,7 @@
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
                         SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMS", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMS", e);
             }
         }
     }
@@ -1980,7 +2105,7 @@
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
                         SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendSMSExpectMore", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendSMSExpectMore", e);
             }
         }
     }
@@ -2011,7 +2136,7 @@
                         isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
                         trafficDescriptor, matchAllRuleAllowed);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "setupDataCall", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setupDataCall", e);
             }
         }
     }
@@ -2048,7 +2173,7 @@
                         RILUtils.convertNullToEmptyString(pin2),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "iccIoForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccIoForApp", e);
             }
         }
     }
@@ -2070,7 +2195,7 @@
             try {
                 voiceProxy.sendUssd(rr.mSerial, RILUtils.convertNullToEmptyString(ussd));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendUssd", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendUssd", e);
             }
         }
     }
@@ -2089,7 +2214,7 @@
             try {
                 voiceProxy.cancelPendingUssd(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "cancelPendingUssd", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "cancelPendingUssd", e);
             }
         }
     }
@@ -2107,7 +2232,7 @@
             try {
                 voiceProxy.getClir(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClir", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClir", e);
             }
         }
     }
@@ -2126,7 +2251,7 @@
             try {
                 voiceProxy.setClir(rr.mSerial, clirMode);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setClir", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setClir", e);
             }
         }
     }
@@ -2147,7 +2272,7 @@
             try {
                 voiceProxy.getCallForwardStatus(rr.mSerial, cfReason, serviceClass, number);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallForwardStatus", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallForwardStatus", e);
             }
         }
     }
@@ -2170,7 +2295,7 @@
                 voiceProxy.setCallForward(
                         rr.mSerial, action, cfReason, serviceClass, number, timeSeconds);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallForward", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallForward", e);
             }
         }
     }
@@ -2190,7 +2315,7 @@
             try {
                 voiceProxy.getCallWaiting(rr.mSerial, serviceClass);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getCallWaiting", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getCallWaiting", e);
             }
         }
     }
@@ -2210,7 +2335,7 @@
             try {
                 voiceProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setCallWaiting", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setCallWaiting", e);
             }
         }
     }
@@ -2231,7 +2356,7 @@
             try {
                 messagingProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
                         "acknowledgeLastIncomingGsmSms", e);
             }
         }
@@ -2251,7 +2376,7 @@
                 voiceProxy.acceptCall(rr.mSerial);
                 mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "acceptCall", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "acceptCall", e);
             }
         }
     }
@@ -2273,7 +2398,7 @@
                 dataProxy.deactivateDataCall(rr.mSerial, cid, reason);
                 mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "deactivateDataCall", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "deactivateDataCall", e);
             }
         }
     }
@@ -2304,7 +2429,7 @@
                         RILUtils.convertNullToEmptyString(password),
                         serviceClass, RILUtils.convertNullToEmptyString(appId));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getFacilityLockForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getFacilityLockForApp", e);
             }
         }
     }
@@ -2335,7 +2460,7 @@
                         RILUtils.convertNullToEmptyString(password), serviceClass,
                         RILUtils.convertNullToEmptyString(appId));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setFacilityLockForApp", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setFacilityLockForApp", e);
             }
         }
     }
@@ -2360,7 +2485,7 @@
                         RILUtils.convertNullToEmptyString(oldPwd),
                         RILUtils.convertNullToEmptyString(newPwd));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "changeBarringPassword", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "changeBarringPassword", e);
             }
         }
     }
@@ -2379,7 +2504,7 @@
             try {
                 networkProxy.getNetworkSelectionMode(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getNetworkSelectionMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getNetworkSelectionMode", e);
             }
         }
     }
@@ -2399,7 +2524,7 @@
                 networkProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(
-                        NETWORK_SERVICE, "setNetworkSelectionModeAutomatic", e);
+                        HAL_SERVICE_NETWORK, "setNetworkSelectionModeAutomatic", e);
             }
         }
     }
@@ -2420,7 +2545,8 @@
                 networkProxy.setNetworkSelectionModeManual(rr.mSerial,
                         RILUtils.convertNullToEmptyString(operatorNumeric), ran);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setNetworkSelectionModeManual", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "setNetworkSelectionModeManual", e);
             }
         }
     }
@@ -2439,7 +2565,7 @@
             try {
                 networkProxy.getAvailableNetworks(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAvailableNetworks", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getAvailableNetworks", e);
             }
         }
     }
@@ -2454,7 +2580,7 @@
     public void startNetworkScan(NetworkScanRequest networkScanRequest, Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
             HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN);
             if (RILJ_LOGD) {
                 riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion);
@@ -2471,7 +2597,7 @@
                 networkProxy.startNetworkScan(rr.mSerial, networkScanRequest, overrideHalVersion,
                         result);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "startNetworkScan", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "startNetworkScan", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNetworkScan: REQUEST_NOT_SUPPORTED");
@@ -2487,7 +2613,7 @@
     public void stopNetworkScan(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
                     mRILDefaultWorkSource);
 
@@ -2498,7 +2624,7 @@
             try {
                 networkProxy.stopNetworkScan(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "stopNetworkScan", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "stopNetworkScan", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNetworkScan: REQUEST_NOT_SUPPORTED");
@@ -2524,7 +2650,7 @@
             try {
                 voiceProxy.startDtmf(rr.mSerial, c + "");
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "startDtmf", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "startDtmf", e);
             }
         }
     }
@@ -2542,7 +2668,7 @@
             try {
                 voiceProxy.stopDtmf(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "stopDtmf", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "stopDtmf", e);
             }
         }
     }
@@ -2562,7 +2688,7 @@
             try {
                 voiceProxy.separateConnection(rr.mSerial, gsmIndex);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "separateConnection", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "separateConnection", e);
             }
         }
     }
@@ -2581,7 +2707,7 @@
             try {
                 modemProxy.getBasebandVersion(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getBasebandVersion", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getBasebandVersion", e);
             }
         }
     }
@@ -2600,7 +2726,7 @@
             try {
                 voiceProxy.setMute(rr.mSerial, enableMute);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setMute", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setMute", e);
             }
         }
     }
@@ -2618,7 +2744,7 @@
             try {
                 voiceProxy.getMute(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getMute", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getMute", e);
             }
         }
     }
@@ -2636,7 +2762,7 @@
             try {
                 voiceProxy.getClip(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getClip", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getClip", e);
             }
         }
     }
@@ -2664,7 +2790,7 @@
             try {
                 dataProxy.getDataCallList(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "getDataCallList", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getDataCallList", e);
             }
         }
     }
@@ -2695,7 +2821,8 @@
             try {
                 networkProxy.setSuppServiceNotifications(rr.mSerial, enable);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setSuppServiceNotifications", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "setSuppServiceNotifications", e);
             }
         }
     }
@@ -2718,7 +2845,7 @@
                         RILUtils.convertNullToEmptyString(smsc),
                         RILUtils.convertNullToEmptyString(pdu));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToSim", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToSim", e);
             }
         }
     }
@@ -2739,7 +2866,7 @@
             try {
                 messagingProxy.deleteSmsOnSim(rr.mSerial, index);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnSim", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnSim", e);
             }
         }
     }
@@ -2758,7 +2885,7 @@
             try {
                 networkProxy.setBandMode(rr.mSerial, bandMode);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setBandMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setBandMode", e);
             }
         }
     }
@@ -2777,7 +2904,7 @@
             try {
                 networkProxy.getAvailableBandModes(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryAvailableBandMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "queryAvailableBandMode", e);
             }
         }
     }
@@ -2797,7 +2924,7 @@
             try {
                 simProxy.sendEnvelope(rr.mSerial, RILUtils.convertNullToEmptyString(contents));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelope", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelope", e);
             }
         }
     }
@@ -2819,7 +2946,7 @@
                 simProxy.sendTerminalResponseToSim(rr.mSerial,
                         RILUtils.convertNullToEmptyString(contents));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "sendTerminalResponse", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendTerminalResponse", e);
             }
         }
     }
@@ -2840,7 +2967,7 @@
                 simProxy.sendEnvelopeWithStatus(rr.mSerial,
                         RILUtils.convertNullToEmptyString(contents));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "sendEnvelopeWithStatus", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "sendEnvelopeWithStatus", e);
             }
         }
     }
@@ -2859,7 +2986,7 @@
             try {
                 voiceProxy.explicitCallTransfer(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "explicitCallTransfer", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "explicitCallTransfer", e);
             }
         }
     }
@@ -2881,7 +3008,7 @@
             try {
                 networkProxy.setPreferredNetworkTypeBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setPreferredNetworkType", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setPreferredNetworkType", e);
             }
         }
     }
@@ -2900,7 +3027,7 @@
             try {
                 networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getPreferredNetworkType", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getPreferredNetworkType", e);
             }
         }
     }
@@ -2910,7 +3037,7 @@
             @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (!networkProxy.isEmpty()) {
-            if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+            if (mHalVersion.get(HAL_SERVICE_NETWORK).less(RADIO_HAL_VERSION_1_6)) {
                 // For older HAL, redirects the call to setPreferredNetworkType.
                 setPreferredNetworkType(
                         RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result);
@@ -2927,7 +3054,8 @@
             try {
                 networkProxy.setAllowedNetworkTypesBitmap(rr.mSerial, mAllowedNetworkTypesBitmask);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setAllowedNetworkTypeBitmask", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "setAllowedNetworkTypeBitmask", e);
             }
         }
     }
@@ -2946,7 +3074,8 @@
             try {
                 networkProxy.getAllowedNetworkTypesBitmap(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getAllowedNetworkTypeBitmask", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "getAllowedNetworkTypeBitmask", e);
             }
         }
     }
@@ -2966,7 +3095,7 @@
             try {
                 networkProxy.setLocationUpdates(rr.mSerial, enable);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setLocationUpdates", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setLocationUpdates", e);
             }
         }
     }
@@ -2978,7 +3107,7 @@
     public void isNrDualConnectivityEnabled(Message result, WorkSource workSource) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result,
                     getDefaultWorkSourceIfInvalid(workSource));
 
@@ -2989,7 +3118,8 @@
             try {
                 networkProxy.isNrDualConnectivityEnabled(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "isNrDualConnectivityEnabled", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "isNrDualConnectivityEnabled", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -3019,7 +3149,7 @@
             WorkSource workSource) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result,
                     getDefaultWorkSourceIfInvalid(workSource));
 
@@ -3031,7 +3161,7 @@
             try {
                 networkProxy.setNrDualConnectivityState(rr.mSerial, (byte) nrDualConnectivityState);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "enableNrDualConnectivity", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "enableNrDualConnectivity", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableNrDualConnectivity: REQUEST_NOT_SUPPORTED");
@@ -3057,7 +3187,7 @@
     @Override
     public void isVoNrEnabled(Message result, WorkSource workSource) {
 
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
             RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
             if (!voiceProxy.isEmpty()) {
                 RILRequest rr = obtainRequest(RIL_REQUEST_IS_VONR_ENABLED , result,
@@ -3070,7 +3200,7 @@
                 try {
                     voiceProxy.isVoNrEnabled(rr.mSerial);
                 } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(VOICE_SERVICE, "isVoNrEnabled", e);
+                    handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "isVoNrEnabled", e);
                 }
             }
         } else {
@@ -3090,7 +3220,7 @@
     public void setVoNrEnabled(boolean enabled, Message result, WorkSource workSource) {
         setVoNrEnabled(enabled);
 
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+        if (mHalVersion.get(HAL_SERVICE_VOICE).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
             RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class, result);
             if (!voiceProxy.isEmpty()) {
                 RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_VONR, result,
@@ -3103,7 +3233,7 @@
                 try {
                     voiceProxy.setVoNrEnabled(rr.mSerial, enabled);
                 } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(VOICE_SERVICE, "setVoNrEnabled", e);
+                    handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setVoNrEnabled", e);
                 }
             }
         } else {
@@ -3135,7 +3265,7 @@
             try {
                 simProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setCdmaSubscriptionSource", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setCdmaSubscriptionSource", e);
             }
         }
     }
@@ -3154,7 +3284,8 @@
             try {
                 networkProxy.getCdmaRoamingPreference(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "queryCdmaRoamingPreference", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "queryCdmaRoamingPreference", e);
             }
         }
     }
@@ -3174,7 +3305,7 @@
             try {
                 networkProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCdmaRoamingPreference", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCdmaRoamingPreference", e);
             }
         }
     }
@@ -3193,7 +3324,7 @@
             try {
                 voiceProxy.getTtyMode(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getTtyMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getTtyMode", e);
             }
         }
     }
@@ -3212,7 +3343,7 @@
             try {
                 voiceProxy.setTtyMode(rr.mSerial, ttyMode);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setTtyMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setTtyMode", e);
             }
         }
     }
@@ -3232,7 +3363,7 @@
             try {
                 voiceProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "setPreferredVoicePrivacy", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "setPreferredVoicePrivacy", e);
             }
         }
     }
@@ -3251,7 +3382,7 @@
             try {
                 voiceProxy.getPreferredVoicePrivacy(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "getPreferredVoicePrivacy", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "getPreferredVoicePrivacy", e);
             }
         }
     }
@@ -3271,7 +3402,7 @@
                 voiceProxy.sendCdmaFeatureCode(rr.mSerial,
                         RILUtils.convertNullToEmptyString(featureCode));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendCdmaFeatureCode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendCdmaFeatureCode", e);
             }
         }
     }
@@ -3292,7 +3423,7 @@
                 voiceProxy.sendBurstDtmf(rr.mSerial, RILUtils.convertNullToEmptyString(dtmfString),
                         on, off);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "sendBurstDtmf", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "sendBurstDtmf", e);
             }
         }
     }
@@ -3312,13 +3443,13 @@
 
             try {
                 messagingProxy.sendCdmaSmsExpectMore(rr.mSerial, pdu);
-                if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                if (mHalVersion.get(HAL_SERVICE_MESSAGING).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
                     mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
                             SmsSession.Event.Format.SMS_FORMAT_3GPP2,
                             getOutgoingSmsMessageId(result));
                 }
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSMSExpectMore", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSMSExpectMore", e);
             }
         }
     }
@@ -3340,7 +3471,7 @@
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
                         SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendCdmaSms", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendCdmaSms", e);
             }
         }
     }
@@ -3361,7 +3492,7 @@
             try {
                 messagingProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, success, cause);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
                         "acknowledgeLastIncomingCdmaSms", e);
             }
         }
@@ -3382,7 +3513,7 @@
             try {
                 messagingProxy.getGsmBroadcastConfig(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getGsmBroadcastConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getGsmBroadcastConfig", e);
             }
         }
     }
@@ -3406,7 +3537,7 @@
             try {
                 messagingProxy.setGsmBroadcastConfig(rr.mSerial, config);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setGsmBroadcastConfig", e);
             }
         }
     }
@@ -3427,7 +3558,8 @@
             try {
                 messagingProxy.setGsmBroadcastActivation(rr.mSerial, activate);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setGsmBroadcastActivation", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
+                        "setGsmBroadcastActivation", e);
             }
         }
     }
@@ -3447,7 +3579,7 @@
             try {
                 messagingProxy.getCdmaBroadcastConfig(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getCdmaBroadcastConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getCdmaBroadcastConfig", e);
             }
         }
     }
@@ -3471,7 +3603,7 @@
             try {
                 messagingProxy.setCdmaBroadcastConfig(rr.mSerial, configs);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setCdmaBroadcastConfig", e);
             }
         }
     }
@@ -3492,7 +3624,8 @@
             try {
                 messagingProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setCdmaBroadcastActivation", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
+                        "setCdmaBroadcastActivation", e);
             }
         }
     }
@@ -3511,7 +3644,7 @@
             try {
                 simProxy.getCdmaSubscription(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscription", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscription", e);
             }
         }
     }
@@ -3532,7 +3665,7 @@
             try {
                 messagingProxy.writeSmsToRuim(rr.mSerial, status, pdu);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "writeSmsToRuim", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "writeSmsToRuim", e);
             }
         }
     }
@@ -3553,7 +3686,7 @@
             try {
                 messagingProxy.deleteSmsOnRuim(rr.mSerial, index);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "deleteSmsOnRuim", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "deleteSmsOnRuim", e);
             }
         }
     }
@@ -3572,7 +3705,7 @@
             try {
                 modemProxy.getDeviceIdentity(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getDeviceIdentity", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getDeviceIdentity", e);
             }
         }
     }
@@ -3591,7 +3724,7 @@
             try {
                 voiceProxy.exitEmergencyCallbackMode(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(VOICE_SERVICE, "exitEmergencyCallbackMode", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_VOICE, "exitEmergencyCallbackMode", e);
             }
         }
     }
@@ -3611,7 +3744,7 @@
             try {
                 messagingProxy.getSmscAddress(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "getSmscAddress", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "getSmscAddress", e);
             }
         }
     }
@@ -3633,7 +3766,7 @@
                 messagingProxy.setSmscAddress(rr.mSerial,
                         RILUtils.convertNullToEmptyString(address));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "setSmscAddress", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "setSmscAddress", e);
             }
         }
     }
@@ -3654,7 +3787,7 @@
             try {
                 messagingProxy.reportSmsMemoryStatus(rr.mSerial, available);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "reportSmsMemoryStatus", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "reportSmsMemoryStatus", e);
             }
         }
     }
@@ -3673,7 +3806,7 @@
             try {
                 simProxy.reportStkServiceIsRunning(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "reportStkServiceIsRunning", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "reportStkServiceIsRunning", e);
             }
         }
     }
@@ -3692,7 +3825,7 @@
             try {
                 simProxy.getCdmaSubscriptionSource(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getCdmaSubscriptionSource", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getCdmaSubscriptionSource", e);
             }
         }
     }
@@ -3714,7 +3847,7 @@
                 messagingProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
                         RILUtils.convertNullToEmptyString(ackPdu));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE,
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING,
                         "acknowledgeIncomingGsmSmsWithPdu", e);
             }
         }
@@ -3734,7 +3867,7 @@
             try {
                 networkProxy.getVoiceRadioTechnology(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getVoiceRadioTechnology", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getVoiceRadioTechnology", e);
             }
         }
     }
@@ -3753,7 +3886,7 @@
             try {
                 networkProxy.getCellInfoList(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getCellInfoList", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getCellInfoList", e);
             }
         }
     }
@@ -3773,7 +3906,7 @@
             try {
                 networkProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setCellInfoListRate", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setCellInfoListRate", e);
             }
         }
     }
@@ -3793,7 +3926,7 @@
             try {
                 dataProxy.setInitialAttachApn(rr.mSerial, dataProfile, isRoaming);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "setInitialAttachApn", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setInitialAttachApn", e);
             }
         }
     }
@@ -3812,7 +3945,7 @@
             try {
                 networkProxy.getImsRegistrationState(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getImsRegistrationState", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getImsRegistrationState", e);
             }
         }
     }
@@ -3835,7 +3968,7 @@
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
                         SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsGsmSms", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsGsmSms", e);
             }
         }
     }
@@ -3857,7 +3990,7 @@
                 mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
                         SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MESSAGING_SERVICE, "sendImsCdmaSms", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MESSAGING, "sendImsCdmaSms", e);
             }
         }
     }
@@ -3885,7 +4018,7 @@
                 simProxy.iccTransmitApduBasicChannel(
                         rr.mSerial, cla, instruction, p1, p2, p3, data);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduBasicChannel", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduBasicChannel", e);
             }
         }
     }
@@ -3910,7 +4043,7 @@
                 simProxy.iccOpenLogicalChannel(rr.mSerial, RILUtils.convertNullToEmptyString(aid),
                         p2);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "iccOpenLogicalChannel", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccOpenLogicalChannel", e);
             }
         }
     }
@@ -3930,7 +4063,7 @@
             try {
                 simProxy.iccCloseLogicalChannel(rr.mSerial, channel);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "iccCloseLogicalChannel", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccCloseLogicalChannel", e);
             }
         }
     }
@@ -3964,7 +4097,7 @@
                 simProxy.iccTransmitApduLogicalChannel(
                         rr.mSerial, channel, cla, instruction, p1, p2, p3, data);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "iccTransmitApduLogicalChannel", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "iccTransmitApduLogicalChannel", e);
             }
         }
     }
@@ -3984,7 +4117,7 @@
             try {
                 modemProxy.nvReadItem(rr.mSerial, itemID);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvReadItem", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvReadItem", e);
             }
         }
     }
@@ -4005,7 +4138,7 @@
                 modemProxy.nvWriteItem(rr.mSerial, itemId,
                         RILUtils.convertNullToEmptyString(itemValue));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteItem", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteItem", e);
             }
         }
     }
@@ -4026,7 +4159,7 @@
             try {
                 modemProxy.nvWriteCdmaPrl(rr.mSerial, preferredRoamingList);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvWriteCdmaPrl", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvWriteCdmaPrl", e);
             }
         }
     }
@@ -4046,7 +4179,7 @@
             try {
                 modemProxy.nvResetConfig(rr.mSerial, resetType);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "nvResetConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "nvResetConfig", e);
             }
         }
     }
@@ -4068,7 +4201,7 @@
             try {
                 simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setUiccSubscription", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setUiccSubscription", e);
             }
         }
     }
@@ -4083,7 +4216,7 @@
         // EID should be supported as long as HAL >= 1.2.
         //  - in HAL 1.2 we have EID through ATR
         //  - in later HAL versions we also have EID through slot / card status.
-        return mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2);
+        return mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2);
     }
 
     @Override
@@ -4100,7 +4233,7 @@
             try {
                 dataProxy.setDataAllowed(rr.mSerial, allowed);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataAllowed", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataAllowed", e);
             }
         }
     }
@@ -4120,7 +4253,7 @@
             try {
                 modemProxy.getHardwareConfig(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getHardwareConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getHardwareConfig", e);
             }
         }
     }
@@ -4143,7 +4276,7 @@
                         RILUtils.convertNullToEmptyString(data),
                         RILUtils.convertNullToEmptyString(aid));
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "requestIccSimAuthentication", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "requestIccSimAuthentication", e);
             }
         }
     }
@@ -4166,7 +4299,7 @@
             try {
                 dataProxy.setDataProfile(rr.mSerial, dps, isRoaming);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataProfile", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataProfile", e);
             }
         }
     }
@@ -4184,7 +4317,7 @@
             try {
                 modemProxy.requestShutdown(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "requestShutdown", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "requestShutdown", e);
             }
         }
     }
@@ -4203,7 +4336,7 @@
             try {
                 modemProxy.getRadioCapability(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getRadioCapability", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getRadioCapability", e);
             }
         }
     }
@@ -4223,14 +4356,14 @@
             try {
                 modemProxy.setRadioCapability(rr.mSerial, rc);
             } catch (Exception e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "setRadioCapability", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "setRadioCapability", e);
             }
         }
     }
 
     @Override
     public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
             // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
             // Instead the LCE functionality is always-on and provides unsolicited indications.
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startLceService: REQUEST_NOT_SUPPORTED");
@@ -4254,14 +4387,14 @@
             try {
                 radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(RADIO_SERVICE, "startLceService", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "startLceService", e);
             }
         }
     }
 
     @Override
     public void stopLceService(Message result) {
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
             // We have a 1.2 or later radio, so the LCE 1.0 LCE service control is unused.
             // Instead the LCE functionality is always-on and provides unsolicited indications.
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopLceService: REQUEST_NOT_SUPPORTED");
@@ -4284,7 +4417,7 @@
             try {
                 radioProxy.stopLceService(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(RADIO_SERVICE, "stopLceService", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "stopLceService", e);
             }
         }
     }
@@ -4303,7 +4436,7 @@
             long completionWindowMillis) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result,
                     getDefaultWorkSourceIfInvalid(workSource));
 
@@ -4318,7 +4451,7 @@
                 dataProxy.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction,
                         completionWindowMillis);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "setDataThrottling", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "setDataThrottling", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "setDataThrottling: REQUEST_NOT_SUPPORTED");
@@ -4343,7 +4476,7 @@
     @Deprecated
     @Override
     public void pullLceData(Message result) {
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+        if (mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
             // We have a 1.2 or later radio, so the LCE 1.0 LCE service control path is unused.
             // Instead the LCE functionality is always-on and provides unsolicited indications.
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "pullLceData: REQUEST_NOT_SUPPORTED");
@@ -4366,7 +4499,7 @@
             try {
                 radioProxy.pullLceData(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(RADIO_SERVICE, "pullLceData", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "pullLceData", e);
             }
         }
     }
@@ -4388,7 +4521,7 @@
                         mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT, rr.mSerial);
                 mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "getModemActivityInfo", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "getModemActivityInfo", e);
             }
         }
     }
@@ -4411,7 +4544,7 @@
             try {
                 simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules, result);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setAllowedCarriers", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setAllowedCarriers", e);
             }
         }
     }
@@ -4430,7 +4563,7 @@
             try {
                 simProxy.getAllowedCarriers(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getAllowedCarriers", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getAllowedCarriers", e);
             }
         }
     }
@@ -4450,7 +4583,7 @@
             try {
                 modemProxy.sendDeviceState(rr.mSerial, stateType, state);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(MODEM_SERVICE, "sendDeviceState", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_MODEM, "sendDeviceState", e);
             }
         }
     }
@@ -4470,7 +4603,7 @@
             try {
                 networkProxy.setIndicationFilter(rr.mSerial, filter);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setIndicationFilter", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setIndicationFilter", e);
             }
         }
     }
@@ -4480,7 +4613,7 @@
             @NonNull List<SignalThresholdInfo> signalThresholdInfos, @Nullable Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
                     result, mRILDefaultWorkSource);
 
@@ -4491,7 +4624,7 @@
             try {
                 networkProxy.setSignalStrengthReportingCriteria(rr.mSerial, signalThresholdInfos);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE,
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
                         "setSignalStrengthReportingCriteria", e);
             }
         } else {
@@ -4505,7 +4638,7 @@
             Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, result,
                     mRILDefaultWorkSource);
 
@@ -4519,7 +4652,7 @@
                         ran);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(
-                        NETWORK_SERVICE, "setLinkCapacityReportingCriteria", e);
+                        HAL_SERVICE_NETWORK, "setLinkCapacityReportingCriteria", e);
             }
         } else {
             riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less than 1.2");
@@ -4541,7 +4674,7 @@
             try {
                 simProxy.setSimCardPower(rr.mSerial, state, result);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setSimCardPower", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "setSimCardPower", e);
             }
         }
     }
@@ -4552,7 +4685,7 @@
         Objects.requireNonNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
                     mRILDefaultWorkSource);
             if (RILJ_LOGD) {
@@ -4562,7 +4695,8 @@
             try {
                 simProxy.setCarrierInfoForImsiEncryption(rr.mSerial, imsiEncryptionInfo);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "setCarrierInfoForImsiEncryption", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM,
+                        "setCarrierInfoForImsiEncryption", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -4582,7 +4716,7 @@
         Objects.requireNonNull(packetData, "KeepaliveRequest cannot be null.");
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_START_KEEPALIVE, result,
                     mRILDefaultWorkSource);
 
@@ -4593,7 +4727,7 @@
             try {
                 dataProxy.startKeepalive(rr.mSerial, contextId, packetData, intervalMillis, result);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "startNattKeepalive", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startNattKeepalive", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startNattKeepalive: REQUEST_NOT_SUPPORTED");
@@ -4609,7 +4743,7 @@
     public void stopNattKeepalive(int sessionHandle, Message result) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_STOP_KEEPALIVE, result,
                     mRILDefaultWorkSource);
 
@@ -4620,7 +4754,7 @@
             try {
                 dataProxy.stopKeepalive(rr.mSerial, sessionHandle);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "stopNattKeepalive", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "stopNattKeepalive", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "stopNattKeepalive: REQUEST_NOT_SUPPORTED");
@@ -4669,7 +4803,7 @@
     public void enableUiccApplications(boolean enable, Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS, result,
                     mRILDefaultWorkSource);
 
@@ -4680,7 +4814,7 @@
             try {
                 simProxy.enableUiccApplications(rr.mSerial, enable);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "enableUiccApplications", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "enableUiccApplications", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "enableUiccApplications: REQUEST_NOT_SUPPORTED");
@@ -4701,7 +4835,7 @@
     public void areUiccApplicationsEnabled(Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT, result,
                     mRILDefaultWorkSource);
 
@@ -4712,7 +4846,7 @@
             try {
                 simProxy.areUiccApplicationsEnabled(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "areUiccApplicationsEnabled", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "areUiccApplicationsEnabled", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -4732,7 +4866,7 @@
     @Override
     public boolean canToggleUiccApplicationsEnablement() {
         return !getRadioServiceProxy(RadioSimProxy.class, null).isEmpty()
-                && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5);
+                && mHalVersion.get(HAL_SERVICE_RADIO).greaterOrEqual(RADIO_HAL_VERSION_1_5);
     }
 
     @Override
@@ -4758,7 +4892,7 @@
                 voiceProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(
-                        VOICE_SERVICE, "handleStkCallSetupRequestFromSim", e);
+                        HAL_SERVICE_VOICE, "handleStkCallSetupRequestFromSim", e);
             }
         }
     }
@@ -4770,7 +4904,7 @@
     public void getBarringInfo(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_BARRING_INFO, result,
                     mRILDefaultWorkSource);
 
@@ -4781,7 +4915,7 @@
             try {
                 networkProxy.getBarringInfo(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getBarringInfo", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getBarringInfo", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getBarringInfo: REQUEST_NOT_SUPPORTED");
@@ -4800,7 +4934,7 @@
     public void allocatePduSessionId(Message result) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result,
                     mRILDefaultWorkSource);
             if (RILJ_LOGD) {
@@ -4810,7 +4944,7 @@
             try {
                 dataProxy.allocatePduSessionId(rr.mSerial);
             } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "allocatePduSessionId", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "allocatePduSessionId", e);
             }
         } else {
             AsyncResult.forMessage(result, null,
@@ -4826,7 +4960,7 @@
     public void releasePduSessionId(Message result, int pduSessionId) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result,
                     mRILDefaultWorkSource);
             if (RILJ_LOGD) {
@@ -4836,7 +4970,7 @@
             try {
                 dataProxy.releasePduSessionId(rr.mSerial, pduSessionId);
             } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "releasePduSessionId", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "releasePduSessionId", e);
             }
         } else {
             AsyncResult.forMessage(result, null,
@@ -4852,7 +4986,7 @@
     public void startHandover(Message result, int callId) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result,
                     mRILDefaultWorkSource);
             if (RILJ_LOGD) {
@@ -4862,7 +4996,7 @@
             try {
                 dataProxy.startHandover(rr.mSerial, callId);
             } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "startHandover", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "startHandover", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startHandover: REQUEST_NOT_SUPPORTED");
@@ -4881,7 +5015,7 @@
     public void cancelHandover(Message result, int callId) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result,
                     mRILDefaultWorkSource);
             if (RILJ_LOGD) {
@@ -4891,7 +5025,7 @@
             try {
                 dataProxy.cancelHandover(rr.mSerial, callId);
             } catch (RemoteException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "cancelHandover", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "cancelHandover", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "cancelHandover: REQUEST_NOT_SUPPORTED");
@@ -4908,7 +5042,7 @@
     public void getSlicingConfig(Message result) {
         RadioDataProxy dataProxy = getRadioServiceProxy(RadioDataProxy.class, result);
         if (dataProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_DATA).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
                     mRILDefaultWorkSource);
 
@@ -4919,7 +5053,7 @@
             try {
                 dataProxy.getSlicingConfig(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(DATA_SERVICE, "getSlicingConfig", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_DATA, "getSlicingConfig", e);
             }
         } else {
             if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getSlicingConfig: REQUEST_NOT_SUPPORTED");
@@ -4933,7 +5067,7 @@
     public void getSimPhonebookRecords(Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
                     mRILDefaultWorkSource);
 
@@ -4944,7 +5078,7 @@
             try {
                 simProxy.getSimPhonebookRecords(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookRecords", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookRecords", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -4962,7 +5096,7 @@
     public void getSimPhonebookCapacity(Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
                     mRILDefaultWorkSource);
 
@@ -4973,7 +5107,7 @@
             try {
                 simProxy.getSimPhonebookCapacity(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "getSimPhonebookCapacity", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "getSimPhonebookCapacity", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -4991,7 +5125,7 @@
     public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
         RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class, result);
         if (simProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+        if (mHalVersion.get(HAL_SERVICE_SIM).greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
                     mRILDefaultWorkSource);
 
@@ -5003,7 +5137,7 @@
             try {
                 simProxy.updateSimPhonebookRecords(rr.mSerial, phonebookRecord);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(SIM_SERVICE, "updateSimPhonebookRecords", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_SIM, "updateSimPhonebookRecords", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -5028,7 +5162,7 @@
             /* @TelephonyManager.UsageSetting */ int usageSetting) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_SET_USAGE_SETTING, result,
                     mRILDefaultWorkSource);
 
@@ -5039,7 +5173,7 @@
             try {
                 networkProxy.setUsageSetting(rr.mSerial, usageSetting);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "setUsageSetting", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setUsageSetting", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -5062,7 +5196,7 @@
     public void getUsageSetting(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
         if (networkProxy.isEmpty()) return;
-        if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_0)) {
             RILRequest rr = obtainRequest(RIL_REQUEST_GET_USAGE_SETTING, result,
                     mRILDefaultWorkSource);
 
@@ -5073,7 +5207,7 @@
             try {
                 networkProxy.getUsageSetting(rr.mSerial);
             } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(NETWORK_SERVICE, "getUsageSetting", e);
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "getUsageSetting", e);
             }
         } else {
             if (RILJ_LOGD) {
@@ -5087,6 +5221,365 @@
         }
     }
 
+    @Override
+    public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SRVCC_CALL_INFO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.setSrvccCallInfo(rr.mSerial,
+                        RILUtils.convertToHalSrvccCall(srvccConnections));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "setSrvccCallInfo", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "setSrvccCallInfo: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void updateImsRegistrationInfo(int state,
+            int accessNetworkType, int suggestedAction, int capabilities, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            android.hardware.radio.ims.ImsRegistration registrationInfo =
+                    new android.hardware.radio.ims.ImsRegistration();
+            registrationInfo.regState = RILUtils.convertImsRegistrationState(state);
+            registrationInfo.accessNetworkType = accessNetworkType;
+            registrationInfo.suggestedAction = suggestedAction;
+            registrationInfo.capabilities = RILUtils.convertImsCapability(capabilities);
+
+            try {
+                imsProxy.updateImsRegistrationInfo(rr.mSerial, registrationInfo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "updateImsRegistrationInfo", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "updateImsRegistrationInfo: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void startImsTraffic(int token, int trafficType, int accessNetworkType, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_START_IMS_TRAFFIC, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                // TODO(ag/20335448): replace 0 with actual trafficDirection
+                imsProxy.startImsTraffic(rr.mSerial, token, trafficType, accessNetworkType, 0);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "startImsTraffic", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "startImsTraffic: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void stopImsTraffic(int token, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_IMS_TRAFFIC, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.stopImsTraffic(rr.mSerial, token);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "stopImsTraffic", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "stopImsTraffic: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void triggerEpsFallback(int reason, Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EPS_FALLBACK, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + " reason=" + reason);
+            }
+
+            try {
+                imsProxy.triggerEpsFallback(rr.mSerial, reason);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "triggerEpsFallback", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "triggerEpsFallback: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    @Override
+    public void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond,
+            Message result) {
+        RadioImsProxy imsProxy = getRadioServiceProxy(RadioImsProxy.class, result);
+        if (imsProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_ANBR_QUERY, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                imsProxy.sendAnbrQuery(rr.mSerial, mediaType, direction, bitsPerSecond);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_IMS, "sendAnbrQuery", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "sendAnbrQuery: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setEmergencyMode(int emcMode, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+        if (networkProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_EMERGENCY_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + " mode=" + EmergencyConstants.emergencyModeToString(emcMode));
+            }
+
+            try {
+                networkProxy.setEmergencyMode(rr.mSerial, emcMode);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "setEmergencyMode", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "setEmergencyMode: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void triggerEmergencyNetworkScan(
+            @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+            @DomainSelectionService.EmergencyScanType int scanType, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+        if (networkProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                networkProxy.triggerEmergencyNetworkScan(rr.mSerial,
+                        RILUtils.convertEmergencyNetworkScanTrigger(accessNetwork, scanType));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "triggerEmergencyNetworkScan", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "triggerEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void cancelEmergencyNetworkScan(boolean resetScan, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+        if (networkProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                        + " resetScan=" + resetScan);
+            }
+
+            try {
+                networkProxy.cancelEmergencyNetworkScan(rr.mSerial, resetScan);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK,
+                        "cancelEmergencyNetworkScan", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "cancelEmergencyNetworkScan: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void exitEmergencyMode(Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+        if (networkProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_IMS).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                networkProxy.exitEmergencyMode(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(HAL_SERVICE_NETWORK, "exitEmergencyMode", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "exitEmergencyMode: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * Set if null ciphering / null integrity modes are permitted.
+     *
+     * @param result Callback message containing the success or failure status.
+     * @param enabled true if null ciphering / null integrity modes are permitted, false otherwise
+     */
+    @Override
+    public void setNullCipherAndIntegrityEnabled(Message result, boolean enabled) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class, result);
+        if (networkProxy.isEmpty()) return;
+        if (mHalVersion.get(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1)) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            }
+
+            try {
+                networkProxy.setNullCipherAndIntegrityEnabled(rr.mSerial, enabled);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(
+                        HAL_SERVICE_NETWORK, "setNullCipherAndIntegrityEnabled", e);
+            }
+        } else {
+            if (RILJ_LOGD) {
+                Rlog.d(RILJ_LOG_TAG, "setNullCipherAndIntegrityEnabled: REQUEST_NOT_SUPPORTED");
+            }
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+    }
+
     //***** Private Methods
     /**
      * This is a helper function to be called when an indication callback is called for any radio
@@ -5128,7 +5621,7 @@
      */
     @VisibleForTesting
     public RILRequest processResponse(RadioResponseInfo responseInfo) {
-        return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error,
+        return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error,
                 responseInfo.type);
     }
 
@@ -5142,7 +5635,7 @@
     @VisibleForTesting
     public RILRequest processResponse_1_6(
             android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
-        return processResponseInternal(RADIO_SERVICE, responseInfo.serial, responseInfo.error,
+        return processResponseInternal(HAL_SERVICE_RADIO, responseInfo.serial, responseInfo.error,
                 responseInfo.type);
     }
 
@@ -5154,7 +5647,6 @@
      * @param responseInfo RadioResponseInfo received in response callback
      * @return RILRequest corresponding to the response
      */
-    @VisibleForTesting
     public RILRequest processResponse(int service,
             android.hardware.radio.RadioResponseInfo responseInfo) {
         return processResponseInternal(service, responseInfo.serial, responseInfo.error,
@@ -5354,13 +5846,13 @@
         RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null,
                 mRILDefaultWorkSource);
         acquireWakeLock(rr, FOR_ACK_WAKELOCK);
-        if (service == RADIO_SERVICE) {
+        if (service == HAL_SERVICE_RADIO) {
             IRadio radioProxy = getRadioProxy(null);
             if (radioProxy != null) {
                 try {
                     radioProxy.responseAcknowledgement();
                 } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(RADIO_SERVICE, "sendAck", e);
+                    handleRadioProxyExceptionForRR(HAL_SERVICE_RADIO, "sendAck", e);
                     riljLoge("sendAck: " + e);
                 }
             } else {
@@ -5931,30 +6423,41 @@
     }
 
     /**
-     * Get the HAL version.
+     * Get the HAL version with a specific service.
      *
+     * @param service the hal service id
      * @return the current HalVersion
      */
-    public HalVersion getHalVersion() {
-        return mRadioVersion;
+    public HalVersion getHalVersion(int service) {
+        HalVersion halVersion = mHalVersion.get(service);
+        if (halVersion == null) {
+            if (isRadioServiceSupported(service)) {
+                halVersion = RADIO_HAL_VERSION_UNKNOWN;
+            } else {
+                halVersion = RADIO_HAL_VERSION_UNSUPPORTED;
+            }
+        }
+        return halVersion;
     }
 
-    private static String serviceToString(int service) {
+    private static String serviceToString(@HalService int service) {
         switch (service) {
-            case RADIO_SERVICE:
+            case HAL_SERVICE_RADIO:
                 return "RADIO";
-            case DATA_SERVICE:
+            case HAL_SERVICE_DATA:
                 return "DATA";
-            case MESSAGING_SERVICE:
+            case HAL_SERVICE_MESSAGING:
                 return "MESSAGING";
-            case MODEM_SERVICE:
+            case HAL_SERVICE_MODEM:
                 return "MODEM";
-            case NETWORK_SERVICE:
+            case HAL_SERVICE_NETWORK:
                 return "NETWORK";
-            case SIM_SERVICE:
+            case HAL_SERVICE_SIM:
                 return "SIM";
-            case VOICE_SERVICE:
+            case HAL_SERVICE_VOICE:
                 return "VOICE";
+            case HAL_SERVICE_IMS:
+                return "IMS";
             default:
                 return "UNKNOWN:" + service;
         }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 61f1e82..d016db8 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -29,6 +29,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_BASEBAND_VERSION;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_HANDOVER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CANCEL_USSD;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_BROADCAST_ACTIVATION;
@@ -74,6 +75,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PUK2;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXIT_EMERGENCY_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_EXPLICIT_CALL_TRANSFER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ACTIVITY_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_ALLOWED_CARRIERS;
@@ -138,6 +140,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RESET_RADIO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SCREEN_STATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_ANBR_QUERY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
@@ -154,6 +157,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_PROFILE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DATA_THROTTLING;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_DC_RT_INFO_RATE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_EMERGENCY_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_FACILITY_LOCK;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA;
@@ -162,12 +166,14 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_MUTE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SRVCC_CALL_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_TTY_MODE;
@@ -185,6 +191,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SMS_ACKNOWLEDGE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_HANDOVER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_IMS_TRAFFIC;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_KEEPALIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN;
@@ -194,12 +201,16 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_SET_PROFILE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_IMS_TRAFFIC;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_KEEPALIVE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_NETWORK_SCAN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_TRIGGER_EPS_FALLBACK;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UDUB;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_RADIO_TECH;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_VOICE_REGISTRATION_STATE;
@@ -214,8 +225,10 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DC_RT_INFO_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NUMBER_LIST;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;
@@ -226,6 +239,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
@@ -260,11 +274,13 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_PROACTIVE_COMMAND;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_END;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.InetAddresses;
 import android.net.LinkAddress;
@@ -298,8 +314,11 @@
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.ClosedSubscriberGroupInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.ModemInfo;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhysicalChannelConfig;
@@ -326,6 +345,7 @@
 import android.telephony.data.RouteSelectionDescriptor;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.data.UrspRule;
+import android.telephony.ims.RegistrationManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -1441,8 +1461,9 @@
                     if (ComprehensionTlvTag.TEXT_STRING.value() == ctlv.getTag()) {
                         byte[] target = Arrays.copyOfRange(ctlv.getRawValue(), from,
                                 ctlv.getValueIndex() + ctlv.getLength());
-                        terminalResponse = terminalResponse.toLowerCase().replace(
-                                IccUtils.bytesToHexString(target).toLowerCase(), "********");
+                        terminalResponse = terminalResponse.toLowerCase(Locale.ROOT).replace(
+                                IccUtils.bytesToHexString(target).toLowerCase(Locale.ROOT),
+                                "********");
                     }
                     // The text string tag and the length field should also be hidden.
                     from = ctlv.getValueIndex() + ctlv.getLength();
@@ -4590,6 +4611,102 @@
                 validationBeforeSwitchSupported, deviceNrCapabilities);
     }
 
+    /**
+     * Convert network scan type
+     * @param scanType The network scan type
+     * @return The converted EmergencyScanType
+     */
+    public static int convertEmergencyScanType(int scanType) {
+        switch (scanType) {
+            case DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE:
+                return android.hardware.radio.network.EmergencyScanType.LIMITED_SERVICE;
+            case DomainSelectionService.SCAN_TYPE_FULL_SERVICE:
+                return android.hardware.radio.network.EmergencyScanType.FULL_SERVICE;
+            default:
+                return android.hardware.radio.network.EmergencyScanType.NO_PREFERENCE;
+        }
+    }
+
+    /**
+     * Convert to EmergencyNetworkScanTrigger
+     * @param accessNetwork The list of access network types
+     * @param scanType The network scan type
+     * @return The converted EmergencyNetworkScanTrigger
+     */
+    public static android.hardware.radio.network.EmergencyNetworkScanTrigger
+            convertEmergencyNetworkScanTrigger(@NonNull int[] accessNetwork, int scanType) {
+        int[] halAccessNetwork = new int[accessNetwork.length];
+        for (int i = 0; i < accessNetwork.length; i++) {
+            halAccessNetwork[i] = convertToHalAccessNetworkAidl(accessNetwork[i]);
+        }
+
+        android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest =
+                new android.hardware.radio.network.EmergencyNetworkScanTrigger();
+
+        scanRequest.accessNetwork = halAccessNetwork;
+        scanRequest.scanType = convertEmergencyScanType(scanType);
+        return scanRequest;
+    }
+
+    /**
+     * Convert EmergencyRegResult.aidl to EmergencyRegResult.
+     * @param halResult EmergencyRegResult.aidl in HAL.
+     * @return Converted EmergencyRegResult.
+     */
+    public static EmergencyRegResult convertHalEmergencyRegResult(
+            android.hardware.radio.network.EmergencyRegResult halResult) {
+        return new EmergencyRegResult(
+                halResult.accessNetwork,
+                convertHalRegState(halResult.regState),
+                halResult.emcDomain,
+                halResult.isVopsSupported,
+                halResult.isEmcBearerSupported,
+                halResult.nwProvidedEmc,
+                halResult.nwProvidedEmf,
+                halResult.mcc,
+                halResult.mnc,
+                getCountryCodeForMccMnc(halResult.mcc, halResult.mnc));
+    }
+
+    private static @NonNull String getCountryCodeForMccMnc(
+            @NonNull String mcc, @NonNull String mnc) {
+        if (TextUtils.isEmpty(mcc)) return "";
+        if (TextUtils.isEmpty(mnc)) mnc = "000";
+        String operatorNumeric = TextUtils.concat(mcc, mnc).toString();
+
+        MccTable.MccMnc mccMnc = MccTable.MccMnc.fromOperatorNumeric(operatorNumeric);
+        return MccTable.geoCountryCodeForMccMnc(mccMnc);
+    }
+
+    /**
+     * Convert RegResult.aidl to RegistrationState.
+     * @param halRegState RegResult in HAL.
+     * @return Converted RegistrationState.
+     */
+    public static @NetworkRegistrationInfo.RegistrationState int convertHalRegState(
+            int halRegState) {
+        switch (halRegState) {
+            case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+            case android.hardware.radio.network.RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+            case android.hardware.radio.network.RegState.REG_HOME:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+            case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP:
+            case android.hardware.radio.network.RegState.NOT_REG_MT_SEARCHING_OP_EM:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
+            case android.hardware.radio.network.RegState.REG_DENIED:
+            case android.hardware.radio.network.RegState.REG_DENIED_EM:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_DENIED;
+            case android.hardware.radio.network.RegState.UNKNOWN:
+            case android.hardware.radio.network.RegState.UNKNOWN_EM:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
+            case android.hardware.radio.network.RegState.REG_ROAMING:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+            default:
+                return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+        }
+    }
+
     /** Append the data to the end of an ArrayList */
     public static void appendPrimitiveArrayToArrayList(byte[] src, ArrayList<Byte> dst) {
         for (byte b : src) {
@@ -5041,6 +5158,28 @@
                 return "SET_USAGE_SETTING";
             case RIL_REQUEST_GET_USAGE_SETTING:
                 return "GET_USAGE_SETTING";
+            case RIL_REQUEST_SET_EMERGENCY_MODE:
+                return "SET_EMERGENCY_MODE";
+            case RIL_REQUEST_TRIGGER_EMERGENCY_NETWORK_SCAN:
+                return "TRIGGER_EMERGENCY_NETWORK_SCAN";
+            case RIL_REQUEST_CANCEL_EMERGENCY_NETWORK_SCAN:
+                return "CANCEL_EMERGENCY_NETWORK_SCAN";
+            case RIL_REQUEST_EXIT_EMERGENCY_MODE:
+                return "EXIT_EMERGENCY_MODE";
+            case RIL_REQUEST_SET_SRVCC_CALL_INFO:
+                return "SET_SRVCC_CALL_INFO";
+            case RIL_REQUEST_UPDATE_IMS_REGISTRATION_INFO:
+                return "UPDATE_IMS_REGISTRATION_INFO";
+            case RIL_REQUEST_START_IMS_TRAFFIC:
+                return "START_IMS_TRAFFIC";
+            case RIL_REQUEST_STOP_IMS_TRAFFIC:
+                return "STOP_IMS_TRAFFIC";
+            case RIL_REQUEST_SEND_ANBR_QUERY:
+                return "SEND_ANBR_QUERY";
+            case RIL_REQUEST_TRIGGER_EPS_FALLBACK:
+                return "TRIGGER_EPS_FALLBACK";
+            case RIL_REQUEST_SET_NULL_CIPHER_AND_INTEGRITY_ENABLED:
+                return "SET_NULL_CIPHER_AND_INTEGRITY_ENABLED";
             default:
                 return "<unknown request " + request + ">";
         }
@@ -5173,6 +5312,14 @@
                 return "UNSOL_REGISTRATION_FAILED";
             case RIL_UNSOL_BARRING_INFO_CHANGED:
                 return "UNSOL_BARRING_INFO_CHANGED";
+            case RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT:
+                return "UNSOL_EMERGENCY_NETWORK_SCAN_RESULT";
+            case RIL_UNSOL_CONNECTION_SETUP_FAILURE:
+                return "UNSOL_CONNECTION_SETUP_FAILURE";
+            case RIL_UNSOL_NOTIFY_ANBR:
+                return "UNSOL_NOTIFY_ANBR";
+            case RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION:
+                return "UNSOL_TRIGGER_IMS_DEREGISTRATION";
             default:
                 return "<unknown response>";
         }
@@ -5321,6 +5468,169 @@
         return sb.toString();
     }
 
+    /**
+     * Converts the list of call information for Single Radio Voice Call Continuity(SRVCC).
+     *
+     * @param srvccConnections The list of call information for SRVCC.
+     * @return The converted list of call information.
+     */
+    public static android.hardware.radio.ims.SrvccCall[] convertToHalSrvccCall(
+            SrvccConnection[] srvccConnections) {
+        if (srvccConnections == null) {
+            return new android.hardware.radio.ims.SrvccCall[0];
+        }
+
+        int length = srvccConnections.length;
+        android.hardware.radio.ims.SrvccCall[] srvccCalls =
+                new android.hardware.radio.ims.SrvccCall[length];
+
+        for (int i = 0; i < length; i++) {
+            srvccCalls[i] = new android.hardware.radio.ims.SrvccCall();
+            srvccCalls[i].index = i + 1;
+            srvccCalls[i].callType = convertSrvccCallType(srvccConnections[i].getType());
+            srvccCalls[i].callState = convertCallState(srvccConnections[i].getState());
+            srvccCalls[i].callSubstate =
+                    convertSrvccCallSubState(srvccConnections[i].getSubState());
+            srvccCalls[i].ringbackToneType =
+                    convertSrvccCallRingbackToneType(srvccConnections[i].getRingbackToneType());
+            srvccCalls[i].isMpty = srvccConnections[i].isMultiParty();
+            srvccCalls[i].isMT = srvccConnections[i].isIncoming();
+            srvccCalls[i].number = TextUtils.emptyIfNull(srvccConnections[i].getNumber());
+            srvccCalls[i].numPresentation =
+                    convertPresentation(srvccConnections[i].getNumberPresentation());
+            srvccCalls[i].name = TextUtils.emptyIfNull(srvccConnections[i].getName());
+            srvccCalls[i].namePresentation =
+                    convertPresentation(srvccConnections[i].getNamePresentation());
+        }
+
+        return srvccCalls;
+    }
+
+    /**
+     * Converts the call type.
+     *
+     * @param type The call type.
+     * @return The converted call type.
+     */
+    public static int convertSrvccCallType(int type) {
+        switch (type) {
+            case  SrvccConnection.CALL_TYPE_NORMAL:
+                return android.hardware.radio.ims.SrvccCall.CallType.NORMAL;
+            case  SrvccConnection.CALL_TYPE_EMERGENCY:
+                return android.hardware.radio.ims.SrvccCall.CallType.EMERGENCY;
+            default:
+                throw new RuntimeException("illegal call type " + type);
+        }
+    }
+
+    /**
+     * Converts the call state.
+     *
+     * @param state The call state.
+     * @return The converted call state.
+     */
+    public static int convertCallState(Call.State state) {
+        switch (state) {
+            case ACTIVE: return android.hardware.radio.voice.Call.STATE_ACTIVE;
+            case HOLDING: return android.hardware.radio.voice.Call.STATE_HOLDING;
+            case DIALING: return android.hardware.radio.voice.Call.STATE_DIALING;
+            case ALERTING: return android.hardware.radio.voice.Call.STATE_ALERTING;
+            case INCOMING: return android.hardware.radio.voice.Call.STATE_INCOMING;
+            case WAITING: return android.hardware.radio.voice.Call.STATE_WAITING;
+            default:
+                throw new RuntimeException("illegal state " + state);
+        }
+    }
+
+    /**
+     * Converts the substate of a call.
+     *
+     * @param state The substate of a call.
+     * @return The converted substate.
+     */
+    public static int convertSrvccCallSubState(int state) {
+        switch (state) {
+            case SrvccConnection.SUBSTATE_NONE:
+                return android.hardware.radio.ims.SrvccCall.CallSubState.NONE;
+            case SrvccConnection.SUBSTATE_PREALERTING:
+                return android.hardware.radio.ims.SrvccCall.CallSubState.PREALERTING;
+            default:
+                throw new RuntimeException("illegal substate " + state);
+        }
+    }
+
+    /**
+     * Converts the ringback tone type.
+     *
+     * @param type The ringback tone type.
+     * @return The converted ringback tone type.
+     */
+    public static int convertSrvccCallRingbackToneType(int type) {
+        switch (type) {
+            case SrvccConnection.TONE_NONE:
+                return android.hardware.radio.ims.SrvccCall.ToneType.NONE;
+            case SrvccConnection.TONE_LOCAL:
+                return android.hardware.radio.ims.SrvccCall.ToneType.LOCAL;
+            case SrvccConnection.TONE_NETWORK:
+                return android.hardware.radio.ims.SrvccCall.ToneType.NETWORK;
+            default:
+                throw new RuntimeException("illegal ringback tone type " + type);
+        }
+    }
+
+    /**
+     * Converts the number presentation type for caller id display.
+     *
+     * @param presentation The number presentation type.
+     * @return The converted presentation type.
+     */
+    public static int convertPresentation(int presentation) {
+        switch (presentation) {
+            case PhoneConstants.PRESENTATION_ALLOWED:
+                return android.hardware.radio.voice.Call.PRESENTATION_ALLOWED;
+            case PhoneConstants.PRESENTATION_RESTRICTED:
+                return android.hardware.radio.voice.Call.PRESENTATION_RESTRICTED;
+            case PhoneConstants.PRESENTATION_UNKNOWN:
+                return android.hardware.radio.voice.Call.PRESENTATION_UNKNOWN;
+            case PhoneConstants.PRESENTATION_PAYPHONE:
+                return android.hardware.radio.voice.Call.PRESENTATION_PAYPHONE;
+            default:
+                throw new RuntimeException("illegal presentation " + presentation);
+        }
+    }
+
+    /** Convert IMS registration state */
+    public static int convertImsRegistrationState(int state) {
+        switch (state) {
+            case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED:
+                return android.hardware.radio.ims.ImsRegistrationState.NOT_REGISTERED;
+            case RegistrationManager.REGISTRATION_STATE_REGISTERED:
+                return android.hardware.radio.ims.ImsRegistrationState.REGISTERED;
+            default:
+                throw new RuntimeException("illegal state " + state);
+        }
+    }
+
+    /** Convert IMS capability */
+    public static int convertImsCapability(int capabilities) {
+        int halCapabilities = android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_NONE;
+        if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE) > 0) {
+            halCapabilities |=
+                    android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VOICE;
+        }
+        if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_VIDEO) > 0) {
+            halCapabilities |=
+                    android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_VIDEO;
+        }
+        if ((capabilities & CommandsInterface.IMS_MMTEL_CAPABILITY_SMS) > 0) {
+            halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_MMTEL_CAPABILITY_SMS;
+        }
+        if ((capabilities & CommandsInterface.IMS_RCS_CAPABILITIES) > 0) {
+            halCapabilities |= android.hardware.radio.ims.ImsRegistration.IMS_RCS_CAPABILITIES;
+        }
+        return halCapabilities;
+    }
+
     private static void logd(String log) {
         Rlog.d("RILUtils", log);
     }
diff --git a/src/java/com/android/internal/telephony/RadioDataProxy.java b/src/java/com/android/internal/telephony/RadioDataProxy.java
index cbc762a..f110545 100644
--- a/src/java/com/android/internal/telephony/RadioDataProxy.java
+++ b/src/java/com/android/internal/telephony/RadioDataProxy.java
@@ -45,12 +45,33 @@
      * Set IRadioData as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param data IRadioData implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) {
+    public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.data.IRadioData data) {
         mHalVersion = halVersion;
         mDataProxy = data;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = data.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioImsProxy.java b/src/java/com/android/internal/telephony/RadioImsProxy.java
new file mode 100644
index 0000000..3ad1b94
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioImsProxy.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+/**
+ * A holder for IRadioIms.
+ * Use getAidl to get IRadioIms and call the AIDL implementations of the HAL APIs.
+ */
+public class RadioImsProxy extends RadioServiceProxy {
+    private static final String TAG = "RadioImsProxy";
+    private volatile android.hardware.radio.ims.IRadioIms mImsProxy = null;
+
+    /**
+     * Sets IRadioIms as the AIDL implementation for RadioServiceProxy.
+     * @param halVersion Radio HAL version.
+     * @param ims IRadioIms implementation.
+     *
+     * @return updated HAL version.
+     */
+    public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.ims.IRadioIms ims) {
+        mHalVersion = halVersion;
+        mImsProxy = ims;
+        mIsAidl = true;
+
+        try {
+            HalVersion newHalVersion;
+            int version = ims.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
+    }
+
+    /**
+     * Gets the AIDL implementation of RadioImsProxy.
+     * @return IRadioIms implementation.
+     */
+    public android.hardware.radio.ims.IRadioIms getAidl() {
+        return mImsProxy;
+    }
+
+    /**
+     * Resets RadioImsProxy.
+     */
+    @Override
+    public void clear() {
+        super.clear();
+        mImsProxy = null;
+    }
+
+    /**
+     * Checks whether a RadioIms implementation exists.
+     * @return true if there is neither a HIDL nor AIDL implementation.
+     */
+    @Override
+    public boolean isEmpty() {
+        return mRadioProxy == null && mImsProxy == null;
+    }
+
+    /**
+     * No implementation in IRadioIms.
+     * @throws RemoteException.
+     */
+    @Override
+    public void responseAcknowledgement() throws RemoteException {
+        /* Currently, IRadioIms doesn't support the following response types:
+         * - RadioIndicationType.UNSOLICITED_ACK_EXP
+         * - RadioResponseType.SOLICITED_ACK_EXP */
+        // no-op
+    }
+
+    /**
+     * Calls IRadioIms#setSrvccCallInfo.
+     * @param serial Serial number of request.
+     * @param srvccCalls The list of call information.
+     * @throws RemoteException.
+     */
+    public void setSrvccCallInfo(int serial,
+            android.hardware.radio.ims.SrvccCall[] srvccCalls) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.setSrvccCallInfo(serial, srvccCalls);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#updateImsRegistrationInfo.
+     * @param serial Serial number of request.
+     * @param registrationInfo The registration state information.
+     * @throws RemoteException.
+     */
+    public void updateImsRegistrationInfo(int serial,
+            android.hardware.radio.ims.ImsRegistration registrationInfo) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.updateImsRegistrationInfo(serial, registrationInfo);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#startImsTraffic.
+     * @param serial Serial number of request.
+     * @param token A nonce to identify the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     * @param accessNetworkType The type of underlying radio access network used.
+     * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+     *        mobile terminated use case eg. MO/MT call/SMS etc.
+     * @throws RemoteException.
+     */
+    public void startImsTraffic(int serial, int token, int trafficType, int accessNetworkType,
+            int trafficDirection) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.startImsTraffic(serial,
+                    token, trafficType, accessNetworkType, trafficDirection);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#stopImsTraffic.
+     * @param serial Serial number of request.
+     * @param token The token assigned by startImsTraffic.
+     * @throws RemoteException.
+     */
+    public void stopImsTraffic(int serial, int token)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.stopImsTraffic(serial, token);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#triggerEpsFallback.
+     * @param serial Serial number of request.
+     * @param reason Specifies the reason for EPS fallback.
+     * @throws RemoteException.
+     */
+    public void triggerEpsFallback(int serial, int reason)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.triggerEpsFallback(serial, reason);
+        }
+    }
+
+    /**
+     * Calls IRadioIms#sendAnbrQuery.
+     * @param serial Serial number of request.
+     * @param mediaType Media type is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The bit rate requested by the opponent UE.
+     * @throws RemoteException.
+     */
+    public void sendAnbrQuery(int serial, int mediaType, int direction, int bitsPerSecond)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.sendAnbrQuery(serial, mediaType, direction, bitsPerSecond);
+        }
+    }
+
+    /**
+     * Call IRadioIms#updateImsCallStatus
+     * @param serial Serial number of request.
+     * @param imsCalls The list of call status information.
+     * @throws RemoteException.
+     */
+    public void updateImsCallStatus(int serial,
+            android.hardware.radio.ims.ImsCall[] imsCalls) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mImsProxy.updateImsCallStatus(serial, imsCalls);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index d947d53..800511b 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
@@ -136,7 +137,7 @@
      * @param radioState android.hardware.radio.V1_0.RadioState
      */
     public void radioStateChanged(int indicationType, int radioState) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int state = RILUtils.convertHalRadioState(radioState);
         if (mRil.isLogOrTrace()) {
@@ -148,7 +149,7 @@
     }
 
     public void callStateChanged(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
 
@@ -160,7 +161,7 @@
      * @param indicationType RadioIndicationType
      */
     public void networkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
 
@@ -168,7 +169,7 @@
     }
 
     public void newSms(int indicationType, ArrayList<Byte> pdu) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu);
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
@@ -181,7 +182,7 @@
     }
 
     public void newSmsStatusReport(int indicationType, ArrayList<Byte> pdu) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         byte[] pduArray = RILUtils.arrayListToPrimitiveArray(pdu);
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
@@ -192,7 +193,7 @@
     }
 
     public void newSmsOnSim(int indicationType, int recordNumber) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
 
@@ -202,7 +203,7 @@
     }
 
     public void onUssd(int indicationType, int ussdModeType, String msg) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
 
@@ -216,7 +217,7 @@
     }
 
     public void nitzTimeReceived(int indicationType, String nitzTime, long receivedTime) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
 
@@ -241,7 +242,7 @@
 
     public void currentSignalStrength(int indicationType,
                                       android.hardware.radio.V1_0.SignalStrength signalStrength) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
 
@@ -259,7 +260,7 @@
      */
     public void currentLinkCapacityEstimate(int indicationType,
                                             android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
 
@@ -275,7 +276,7 @@
      */
     public void currentLinkCapacityEstimate_1_6(int indicationType,
             android.hardware.radio.V1_6.LinkCapacityEstimate lce) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
 
@@ -291,7 +292,7 @@
      */
     public void currentSignalStrength_1_2(int indicationType,
                                       android.hardware.radio.V1_2.SignalStrength signalStrength) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
         // Note this is set to "verbose" because it happens frequently
@@ -308,7 +309,7 @@
     public void currentSignalStrength_1_4(int indicationType,
             android.hardware.radio.V1_4.SignalStrength signalStrength) {
 
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
 
@@ -325,7 +326,7 @@
     public void currentSignalStrength_1_6(int indicationType,
             android.hardware.radio.V1_6.SignalStrength signalStrength) {
 
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         SignalStrength ss = RILUtils.convertHalSignalStrength(signalStrength);
 
@@ -341,7 +342,7 @@
      */
     public void currentPhysicalChannelConfigs_1_4(int indicationType,
             ArrayList<android.hardware.radio.V1_4.PhysicalChannelConfig> configs) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         physicalChannelConfigsIndication(configs);
     }
 
@@ -350,7 +351,7 @@
      */
     public void currentPhysicalChannelConfigs_1_6(int indicationType,
             ArrayList<android.hardware.radio.V1_6.PhysicalChannelConfig> configs) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         physicalChannelConfigsIndication(configs);
     }
 
@@ -359,7 +360,7 @@
      */
     public void currentPhysicalChannelConfigs(int indicationType,
             ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         physicalChannelConfigsIndication(configs);
     }
 
@@ -368,7 +369,7 @@
      */
     public void currentEmergencyNumberList(int indicationType,
             ArrayList<android.hardware.radio.V1_4.EmergencyNumber> emergencyNumberList) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.size());
 
         for (android.hardware.radio.V1_4.EmergencyNumber emergencyNumberHal
@@ -422,7 +423,7 @@
     }
 
     public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         SuppServiceNotification notification = new SuppServiceNotification();
         notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
@@ -441,7 +442,7 @@
     }
 
     public void stkSessionEnd(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
 
@@ -451,7 +452,7 @@
     }
 
     public void stkProactiveCommand(int indicationType, String cmd) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
 
@@ -461,7 +462,7 @@
     }
 
     public void stkEventNotify(int indicationType, String cmd) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
 
@@ -471,7 +472,7 @@
     }
 
     public void stkCallSetup(int indicationType, long timeout) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
 
@@ -481,7 +482,7 @@
     }
 
     public void simSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
 
@@ -491,7 +492,7 @@
     }
 
     public void simRefresh(int indicationType, SimRefreshResult refreshResult) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         IccRefreshResponse response = new IccRefreshResponse();
         response.refreshResult = refreshResult.type;
@@ -504,7 +505,7 @@
     }
 
     public void callRing(int indicationType, boolean isGsm, CdmaSignalInfoRecord record) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         char response[] = null;
 
@@ -527,7 +528,7 @@
     }
 
     public void simStatusChanged(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
 
@@ -535,7 +536,7 @@
     }
 
     public void cdmaNewSms(int indicationType, CdmaSmsMessage msg) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
 
@@ -546,7 +547,7 @@
     }
 
     public void newBroadcastSms(int indicationType, ArrayList<Byte> data) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         byte[] response = RILUtils.arrayListToPrimitiveArray(data);
         if (mRil.isLogOrTrace()) {
@@ -560,7 +561,7 @@
     }
 
     public void cdmaRuimSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
 
@@ -570,7 +571,7 @@
     }
 
     public void restrictedStateChanged(int indicationType, int state) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
 
@@ -580,7 +581,7 @@
     }
 
     public void enterEmergencyCallbackMode(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
 
@@ -590,7 +591,7 @@
     }
 
     public void cdmaCallWaiting(int indicationType, CdmaCallWaiting callWaitingRecord) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         // todo: create a CdmaCallWaitingNotification constructor that takes in these fields to make
         // sure no fields are missing
@@ -614,7 +615,7 @@
     }
 
     public void cdmaOtaProvisionStatus(int indicationType, int status) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = status;
@@ -628,7 +629,7 @@
 
     public void cdmaInfoRec(int indicationType,
                             android.hardware.radio.V1_0.CdmaInformationRecords records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int numberOfInfoRecs = records.infoRec.size();
         for (int i = 0; i < numberOfInfoRecs; i++) {
@@ -724,7 +725,7 @@
     }
 
     public void indicateRingbackTone(int indicationType, boolean start) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
 
@@ -732,7 +733,7 @@
     }
 
     public void resendIncallMute(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
 
@@ -740,7 +741,7 @@
     }
 
     public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = cdmaSource;
@@ -754,7 +755,7 @@
     }
 
     public void cdmaPrlChanged(int indicationType, int version) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = version;
@@ -766,7 +767,7 @@
     }
 
     public void exitEmergencyCallbackMode(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
 
@@ -774,7 +775,7 @@
     }
 
     public void rilConnected(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
 
@@ -787,7 +788,7 @@
     }
 
     public void voiceRadioTechChanged(int indicationType, int rat) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = rat;
@@ -803,35 +804,35 @@
     /** Get unsolicited message for cellInfoList */
     public void cellInfoList(int indicationType,
             ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         responseCellInfoList(records);
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_2 */
     public void cellInfoList_1_2(int indicationType,
             ArrayList<android.hardware.radio.V1_2.CellInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         responseCellInfoList(records);
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_4 */
     public void cellInfoList_1_4(int indicationType,
             ArrayList<android.hardware.radio.V1_4.CellInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         responseCellInfoList(records);
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_5 */
     public void cellInfoList_1_5(int indicationType,
             ArrayList<android.hardware.radio.V1_5.CellInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         responseCellInfoList(records);
     }
 
     /** Get unsolicited message for cellInfoList using HAL V1_5 */
     public void cellInfoList_1_6(int indicationType,
             ArrayList<android.hardware.radio.V1_6.CellInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         responseCellInfoList(records);
     }
 
@@ -843,7 +844,7 @@
 
     /** Get unsolicited message for uicc applications enablement changes. */
     public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled);
@@ -883,7 +884,7 @@
     }
 
     public void imsNetworkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
 
@@ -891,7 +892,7 @@
     }
 
     public void subscriptionStatusChanged(int indicationType, boolean activate) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = activate ? 1 : 0;
@@ -905,7 +906,7 @@
     }
 
     public void srvccStateNotify(int indicationType, int state) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int response[] = new int[1];
         response[0] = state;
@@ -921,7 +922,7 @@
     public void hardwareConfigChanged(
             int indicationType,
             ArrayList<android.hardware.radio.V1_0.HardwareConfig> configs) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs);
 
@@ -933,7 +934,7 @@
 
     public void radioCapabilityIndication(int indicationType,
                                           android.hardware.radio.V1_0.RadioCapability rc) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         RadioCapability response = RILUtils.convertHalRadioCapability(rc, mRil);
 
@@ -944,7 +945,7 @@
     }
 
     public void onSupplementaryServiceIndication(int indicationType, StkCcUnsolSsResult ss) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         int num;
         SsData ssData = new SsData();
@@ -992,7 +993,7 @@
     }
 
     public void stkCallControlAlphaNotify(int indicationType, String alpha) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
 
@@ -1002,7 +1003,7 @@
     }
 
     public void lceData(int indicationType, LceDataInfo lce) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
 
@@ -1014,7 +1015,7 @@
     }
 
     public void pcoData(int indicationType, PcoDataInfo pco) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId,
                 RILUtils.arrayListToPrimitiveArray(pco.contents));
@@ -1025,7 +1026,7 @@
     }
 
     public void modemReset(int indicationType, String reason) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
 
@@ -1038,7 +1039,7 @@
      * @param indicationType RadioIndicationType
      */
     public void carrierInfoForImsiEncryption(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
@@ -1055,7 +1056,7 @@
      */
     public void keepaliveStatus(
             int indicationType, android.hardware.radio.V1_1.KeepaliveStatus halStatus) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(
@@ -1074,7 +1075,7 @@
      * @param indicationType RadioIndicationType
      */
     public void simPhonebookChanged(int indicationType) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
@@ -1091,7 +1092,7 @@
      */
     public void simPhonebookRecordsReceived(int indicationType, byte status,
             ArrayList<PhonebookRecordInfo> records) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>();
 
@@ -1124,7 +1125,7 @@
             android.hardware.radio.V1_5.CellIdentity cellIdentity, String chosenPlmn,
             @NetworkRegistrationInfo.Domain int domain,
             int causeCode, int additionalCauseCode) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
         CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity);
         if (ci == null
                 || TextUtils.isEmpty(chosenPlmn)
@@ -1155,7 +1156,7 @@
     public void barringInfoChanged(int indicationType,
             android.hardware.radio.V1_5.CellIdentity cellIdentity,
             ArrayList<android.hardware.radio.V1_5.BarringInfo> barringInfos) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (cellIdentity == null || barringInfos == null) {
             reportAnomaly(
@@ -1270,7 +1271,7 @@
 
     private void responseNetworkScan(int indicationType,
             android.hardware.radio.V1_1.NetworkScanResult result) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<CellInfo> cellInfos =
                 RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
@@ -1281,7 +1282,7 @@
 
     private void responseNetworkScan_1_2(int indicationType,
             android.hardware.radio.V1_2.NetworkScanResult result) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<CellInfo> cellInfos =
                 RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
@@ -1292,7 +1293,7 @@
 
     private void responseNetworkScan_1_4(int indicationType,
             android.hardware.radio.V1_4.NetworkScanResult result) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<CellInfo> cellInfos =
                 RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
@@ -1303,7 +1304,7 @@
 
     private void responseNetworkScan_1_5(int indicationType,
             android.hardware.radio.V1_5.NetworkScanResult result) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<CellInfo> cellInfos =
                 RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
@@ -1314,7 +1315,7 @@
 
     private void responseNetworkScan_1_6(int indicationType,
             android.hardware.radio.V1_6.NetworkScanResult result) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         ArrayList<CellInfo> cellInfos =
                 RILUtils.convertHalCellInfoList(new ArrayList<>(result.networkInfos));
@@ -1324,7 +1325,7 @@
     }
 
     private void responseDataCallListChanged(int indicationType, List<?> dcList) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
 
@@ -1334,7 +1335,7 @@
     }
 
     private void responseApnUnthrottled(int indicationType, String apn) {
-        mRil.processIndication(RIL.RADIO_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_RADIO, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn);
 
diff --git a/src/java/com/android/internal/telephony/RadioMessagingProxy.java b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
index e68e957..4e9bc8c 100644
--- a/src/java/com/android/internal/telephony/RadioMessagingProxy.java
+++ b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
@@ -36,13 +36,34 @@
      * Set IRadioMessaging as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param messaging IRadioMessaging implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion,
+    public HalVersion setAidl(HalVersion halVersion,
             android.hardware.radio.messaging.IRadioMessaging messaging) {
         mHalVersion = halVersion;
         mMessagingProxy = messaging;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = messaging.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioModemProxy.java b/src/java/com/android/internal/telephony/RadioModemProxy.java
index 7aaf727..a9558a5 100644
--- a/src/java/com/android/internal/telephony/RadioModemProxy.java
+++ b/src/java/com/android/internal/telephony/RadioModemProxy.java
@@ -31,13 +31,34 @@
      * Set IRadioModem as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param modem IRadioModem implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion,
+    public HalVersion setAidl(HalVersion halVersion,
             android.hardware.radio.modem.IRadioModem modem) {
         mHalVersion = halVersion;
         mModemProxy = modem;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = modem.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
index b881035..c8855cd 100644
--- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java
+++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -65,13 +65,37 @@
      * Set IRadioNetwork as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param network IRadioNetwork implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion,
+    public HalVersion setAidl(HalVersion halVersion,
             android.hardware.radio.network.IRadioNetwork network) {
         mHalVersion = halVersion;
         mNetworkProxy = network;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = network.getInterfaceVersion();
+            switch(version) {
+                case 2:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_1;
+                    break;
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
@@ -828,4 +852,84 @@
         }
         // Only supported on AIDL.
     }
+
+    /**
+     * Set the Emergency Mode
+     *
+     * @param serial Serial number of the request.
+     * @param emcModeType Defines the radio emergency mode type.
+     * @throws RemoteException
+     */
+    public void setEmergencyMode(int serial, int emcModeType) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.setEmergencyMode(serial, emcModeType);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Triggers an Emergency network scan.
+     *
+     * @param serial Serial number of the request.
+     * @param scanRequest Contains the preferred networks and type of service to be scanned.
+     * @throws RemoteException
+     */
+    public void triggerEmergencyNetworkScan(int serial,
+            android.hardware.radio.network.EmergencyNetworkScanTrigger scanRequest)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.triggerEmergencyNetworkScan(serial, scanRequest);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Cancels ongoing Emergency network scan
+     *
+     * @param serial Serial number of the request.
+     * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+     *        If {@code true}, then the modem shall start the new scan from the beginning,
+     *        otherwise the modem shall resume from the last search.
+     *
+     * @throws RemoteException
+     */
+    public void cancelEmergencyNetworkScan(int serial, boolean resetScan) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.cancelEmergencyNetworkScan(serial, resetScan);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Exits ongoing Emergency Mode
+     *
+     * @param serial Serial number of the request.
+     * @throws RemoteException
+     */
+    public void exitEmergencyMode(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.exitEmergencyMode(serial);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Set if null ciphering / null integrity is permitted.
+     *
+     * @param serial Serial number of the request.
+     * @param enabled true if null modes are allowed, false otherwise
+     * @throws RemoteException
+     */
+    public void setNullCipherAndIntegrityEnabled(int serial,
+            boolean enabled) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.setNullCipherAndIntegrityEnabled(serial, enabled);
+        }
+        // Only supported on AIDL.
+    }
 }
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java
index da5b660..d1bb6b1 100644
--- a/src/java/com/android/internal/telephony/RadioSimProxy.java
+++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -41,12 +41,33 @@
      * Set IRadioSim as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param sim IRadioSim implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) {
+    public HalVersion setAidl(HalVersion halVersion, android.hardware.radio.sim.IRadioSim sim) {
         mHalVersion = halVersion;
         mSimProxy = sim;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = sim.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioVoiceProxy.java b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
index 6ac603b..07afaad 100644
--- a/src/java/com/android/internal/telephony/RadioVoiceProxy.java
+++ b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
@@ -35,12 +35,34 @@
      * Set IRadioVoice as the AIDL implementation for RadioServiceProxy
      * @param halVersion Radio HAL version
      * @param voice IRadioVoice implementation
+     *
+     * @return updated HAL version
      */
-    public void setAidl(HalVersion halVersion, android.hardware.radio.voice.IRadioVoice voice) {
+    public HalVersion setAidl(HalVersion halVersion,
+            android.hardware.radio.voice.IRadioVoice voice) {
         mHalVersion = halVersion;
         mVoiceProxy = voice;
         mIsAidl = true;
-        Rlog.d(TAG, "AIDL initialized");
+
+        try {
+            HalVersion newHalVersion;
+            int version = voice.getInterfaceVersion();
+            switch(version) {
+                default:
+                    newHalVersion = RIL.RADIO_HAL_VERSION_2_0;
+                    break;
+            }
+            Rlog.d(TAG, "AIDL version=" + version + ", halVersion=" + newHalVersion);
+
+            if (mHalVersion.less(newHalVersion)) {
+                mHalVersion = newHalVersion;
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "setAidl: " + e);
+        }
+
+        Rlog.d(TAG, "AIDL initialized mHalVersion=" + mHalVersion);
+        return mHalVersion;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
deleted file mode 100644
index 83864e4..0000000
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/**
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.os.Build;
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.telephony.dataconnection.DataThrottler;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-import java.util.ArrayList;
-import java.util.Random;
-
-/**
- * Retry manager allows a simple way to declare a series of
- * retry timeouts. After creating a RetryManager the configure
- * method is used to define the sequence. A simple linear series
- * may be initialized using configure with three integer parameters
- * The other configure method allows a series to be declared using
- * a string.
- *<p>
- * The format of the configuration string is the apn type followed by a series of parameters
- * separated by a comma. There are two name value pair parameters plus a series
- * of delay times. The units of of these delay times is unspecified.
- * The name value pairs which may be specified are:
- *<ul>
- *<li>max_retries=<value>
- *<li>default_randomizationTime=<value>
- *</ul>
- *<p>
- * apn type specifies the APN type that the retry pattern will apply for. "others" is for all other
- * APN types not specified in the config.
- *
- * max_retries is the number of times that incrementRetryCount
- * maybe called before isRetryNeeded will return false. if value
- * is infinite then isRetryNeeded will always return true.
- *
- * default_randomizationTime will be used as the randomizationTime
- * for delay times which have no supplied randomizationTime. If
- * default_randomizationTime is not defined it defaults to 0.
- *<p>
- * The other parameters define The series of delay times and each
- * may have an optional randomization value separated from the
- * delay time by a colon.
- *<p>
- * Examples:
- * <ul>
- * <li>3 retries for mms with no randomization value which means its 0:
- * <ul><li><code>"mms:1000, 2000, 3000"</code></ul>
- *
- * <li>10 retries for default APN with a 500 default randomization value for each and
- * the 4..10 retries all using 3000 as the delay:
- * <ul><li><code>"default:max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul>
- *
- * <li>4 retries for supl APN with a 100 as the default randomization value for the first 2 values
- * and the other two having specified values of 500:
- * <ul><li><code>"supl:default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul>
- *
- * <li>Infinite number of retries for all other APNs with the first one at 1000, the second at 2000
- * all others will be at 3000.
- * <ul><li><code>"others:max_retries=infinite,1000,2000,3000</code></ul>
- * </ul>
- *
- * {@hide}
- */
-public class RetryManager {
-    public static final String LOG_TAG = "RetryManager";
-    public static final boolean DBG = true;
-    public static final boolean VDBG = false; // STOPSHIP if true
-
-    /**
-     * The default retry configuration for APNs. See above for the syntax.
-     */
-    private static final String DEFAULT_DATA_RETRY_CONFIG = "max_retries=3, 5000, 5000, 5000";
-
-    /**
-     * The APN type used for all other APNs retry configuration.
-     */
-    private static final String OTHERS_APN_TYPE = "others";
-
-    /**
-     * The default value (in milliseconds) for delay between APN trying (mInterApnDelay)
-     * within the same round
-     */
-    private static final long DEFAULT_INTER_APN_DELAY = 20000;
-
-    /**
-     * The default value (in milliseconds) for delay between APN trying (mFailFastInterApnDelay)
-     * within the same round when we are in fail fast mode
-     */
-    private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000;
-
-    /**
-     * The default value (in milliseconds) for retrying APN after disconnect
-     */
-    private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
-
-    /**
-     * The value indicating retry should not occur.
-     */
-    public static final long NO_RETRY = Long.MAX_VALUE;
-
-    /**
-     * The value indicating network did not suggest any retry delay
-     */
-    public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_DURATION_UNDEFINED;
-
-    /**
-     * If the network suggests a retry delay in the data call setup response, we will retry
-     * the current APN setting again. The maximum retry count is to prevent that network
-     * keeps asking device to retry data setup forever and causes power consumption issue.
-     */
-    private static final int DEFAULT_MAX_SAME_APN_RETRY = 3;
-
-    /**
-     * The delay (in milliseconds) between APN trying within the same round
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private long mInterApnDelay;
-
-    /**
-     * The delay (in milliseconds) between APN trying within the same round when we are in
-     * fail fast mode
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private long mFailFastInterApnDelay;
-
-    /**
-     * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports
-     * data call lost)
-     */
-    private long mApnRetryAfterDisconnectDelay;
-
-    /**
-     * The counter for same APN retrying. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
-     */
-    private int mSameApnRetryCount = 0;
-
-    /**
-     * The maximum times that frameworks retries data setup with the same APN. This value could be
-     * changed via carrier config. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
-     */
-    private int mMaxSameApnRetry = DEFAULT_MAX_SAME_APN_RETRY;
-
-    /**
-     * Retry record with times in milli-seconds
-     */
-    private static class RetryRec {
-        long mDelayTime;
-        long mRandomizationTime;
-
-        RetryRec(long delayTime, long randomizationTime) {
-            mDelayTime = delayTime;
-            mRandomizationTime = randomizationTime;
-        }
-    }
-
-    /**
-     * The array of retry records
-     */
-    private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>();
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Phone mPhone;
-
-    private final DataThrottler mDataThrottler;
-
-    /**
-     * Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount
-     */
-    private boolean mRetryForever = false;
-
-    /**
-     * The maximum number of retries to attempt
-     */
-    private int mMaxRetryCount;
-
-    /**
-     * The current number of retries
-     */
-    private int mRetryCount = 0;
-
-    /**
-     * Random number generator. The random delay will be added into retry timer to avoid all devices
-     * around retrying the APN at the same time.
-     */
-    private Random mRng = new Random();
-
-    /**
-     * Retry manager configuration string. See top of the detailed explanation.
-     */
-    private String mConfig;
-
-    /**
-     * The list to store APN setting candidates for data call setup. Most of the carriers only have
-     * one APN, but few carriers have more than one.
-     */
-    private ArrayList<ApnSetting> mWaitingApns = new ArrayList<>();
-
-    /**
-     * Index pointing to the current trying APN from mWaitingApns
-     */
-    private int mCurrentApnIndex = -1;
-
-    /**
-     * Apn context type.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private String mApnType;
-
-    private final @ApnType int apnType;
-
-    /**
-     * Retry manager constructor
-     * @param phone Phone object
-     * @param dataThrottler Data throttler
-     * @param apnType APN type
-     */
-    public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler,
-            @ApnType int apnType) {
-        mPhone = phone;
-        mDataThrottler = dataThrottler;
-        this.apnType = apnType;
-    }
-
-    /**
-     * Configure for using string which allow arbitrary
-     * sequences of times. See class comments for the
-     * string format.
-     *
-     * @return true if successful
-     */
-    @UnsupportedAppUsage
-    private boolean configure(String configStr) {
-        // Strip quotes if present.
-        if ((configStr.startsWith("\"") && configStr.endsWith("\""))) {
-            configStr = configStr.substring(1, configStr.length() - 1);
-        }
-
-        // Reset the retry manager since delay, max retry count, etc...will be reset.
-        reset();
-
-        if (DBG) log("configure: '" + configStr + "'");
-        mConfig = configStr;
-
-        if (!TextUtils.isEmpty(configStr)) {
-            long defaultRandomization = 0;
-
-            if (VDBG) log("configure: not empty");
-
-            String strArray[] = configStr.split(",");
-            for (int i = 0; i < strArray.length; i++) {
-                if (VDBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
-                Pair<Boolean, Integer> value;
-                String splitStr[] = strArray[i].split("=", 2);
-                splitStr[0] = splitStr[0].trim();
-                if (VDBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
-                if (splitStr.length > 1) {
-                    splitStr[1] = splitStr[1].trim();
-                    if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
-                    if (TextUtils.equals(splitStr[0], "default_randomization")) {
-                        value = parseNonNegativeInt(splitStr[0], splitStr[1]);
-                        if (!value.first) return false;
-                        defaultRandomization = value.second;
-                    } else if (TextUtils.equals(splitStr[0], "max_retries")) {
-                        if (TextUtils.equals("infinite", splitStr[1])) {
-                            mRetryForever = true;
-                        } else {
-                            value = parseNonNegativeInt(splitStr[0], splitStr[1]);
-                            if (!value.first) return false;
-                            mMaxRetryCount = value.second;
-                        }
-                    } else {
-                        Rlog.e(LOG_TAG, "Unrecognized configuration name value pair: "
-                                        + strArray[i]);
-                        return false;
-                    }
-                } else {
-                    /**
-                     * Assume a retry time with an optional randomization value
-                     * following a ":"
-                     */
-                    splitStr = strArray[i].split(":", 2);
-                    splitStr[0] = splitStr[0].trim();
-                    RetryRec rr = new RetryRec(0, 0);
-                    value = parseNonNegativeInt("delayTime", splitStr[0]);
-                    if (!value.first) return false;
-                    rr.mDelayTime = value.second;
-
-                    // Check if optional randomization value present
-                    if (splitStr.length > 1) {
-                        splitStr[1] = splitStr[1].trim();
-                        if (VDBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
-                        value = parseNonNegativeInt("randomizationTime", splitStr[1]);
-                        if (!value.first) return false;
-                        rr.mRandomizationTime = value.second;
-                    } else {
-                        rr.mRandomizationTime = defaultRandomization;
-                    }
-                    mRetryArray.add(rr);
-                }
-            }
-            if (mRetryArray.size() > mMaxRetryCount) {
-                mMaxRetryCount = mRetryArray.size();
-                if (VDBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
-            }
-        } else {
-            log("configure: cleared");
-        }
-
-        if (VDBG) log("configure: true");
-        return true;
-    }
-
-    /**
-     * Configure the retry manager
-     */
-    private void configureRetry() {
-        String configString = null;
-        String otherConfigString = null;
-
-        try {
-            if (TelephonyUtils.IS_DEBUGGABLE) {
-                // Using system properties is easier for testing from command line.
-                String config = SystemProperties.get("test.data_retry_config");
-                if (!TextUtils.isEmpty(config)) {
-                    configure(config);
-                    return;
-                }
-            }
-
-            CarrierConfigManager configManager = (CarrierConfigManager)
-                    mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
-
-            mInterApnDelay = b.getLong(
-                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG,
-                    DEFAULT_INTER_APN_DELAY);
-            mFailFastInterApnDelay = b.getLong(
-                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG,
-                    DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);
-            mApnRetryAfterDisconnectDelay = b.getLong(
-                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
-                    DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
-            mMaxSameApnRetry = b.getInt(
-                    CarrierConfigManager
-                            .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
-                    DEFAULT_MAX_SAME_APN_RETRY);
-
-            // Load all retry patterns for all different APNs.
-            String[] allConfigStrings = b.getStringArray(
-                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS);
-            if (allConfigStrings != null) {
-                for (String s : allConfigStrings) {
-                    if (!TextUtils.isEmpty(s)) {
-                        String splitStr[] = s.split(":", 2);
-                        if (splitStr.length == 2) {
-                            String apnTypeStr = splitStr[0].trim();
-                            // Check if this retry pattern is for the APN we want.
-                            if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) {
-                                // Extract the config string. Note that an empty string is valid
-                                // here, meaning no retry for the specified APN.
-                                configString = splitStr[1];
-                                break;
-                            } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) {
-                                // Extract the config string. Note that an empty string is valid
-                                // here, meaning no retry for all other APNs.
-                                otherConfigString = splitStr[1];
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (configString == null) {
-                if (otherConfigString != null) {
-                    configString = otherConfigString;
-                } else {
-                    // We should never reach here. If we reach here, it must be a configuration
-                    // error bug.
-                    log("Invalid APN retry configuration!. Use the default one now.");
-                    configString = DEFAULT_DATA_RETRY_CONFIG;
-                }
-            }
-        } catch (NullPointerException ex) {
-            // We should never reach here unless there is a bug
-            log("Failed to read configuration! Use the hardcoded default value.");
-
-            mInterApnDelay = DEFAULT_INTER_APN_DELAY;
-            mFailFastInterApnDelay = DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING;
-            configString = DEFAULT_DATA_RETRY_CONFIG;
-        }
-
-        if (VDBG) {
-            log("mInterApnDelay = " + mInterApnDelay + ", mFailFastInterApnDelay = " +
-                    mFailFastInterApnDelay);
-        }
-
-        configure(configString);
-    }
-
-    /**
-     * Return the timer that should be used to trigger the data reconnection
-     */
-    @UnsupportedAppUsage
-    private long getRetryTimer() {
-        int index;
-        if (mRetryCount < mRetryArray.size()) {
-            index = mRetryCount;
-        } else {
-            index = mRetryArray.size() - 1;
-        }
-
-        long retVal;
-        if ((index >= 0) && (index < mRetryArray.size())) {
-            retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
-        } else {
-            retVal = 0;
-        }
-
-        if (DBG) log("getRetryTimer: " + retVal);
-        return retVal;
-    }
-
-    /**
-     * Parse an integer validating the value is not negative.
-     * @param name Name
-     * @param stringValue Value
-     * @return Pair.first == true if stringValue an integer >= 0
-     */
-    private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) {
-        int value;
-        Pair<Boolean, Integer> retVal;
-        try {
-            value = Integer.parseInt(stringValue);
-            retVal = new Pair<>(validateNonNegativeInt(name, value), value);
-        } catch (NumberFormatException e) {
-            Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e);
-            retVal = new Pair<>(false, 0);
-        }
-        if (VDBG) {
-            log("parseNonNegativeInt: " + name + ", " + stringValue + ", "
-                    + retVal.first + ", " + retVal.second);
-        }
-        return retVal;
-    }
-
-    /**
-     * Validate an integer is >= 0 and logs an error if not
-     * @param name Name
-     * @param value Value
-     * @return Pair.first
-     */
-    private boolean validateNonNegativeInt(String name, long value) {
-        boolean retVal;
-        if (value < 0) {
-            Rlog.e(LOG_TAG, name + " bad value: is < 0");
-            retVal = false;
-        } else {
-            retVal = true;
-        }
-        if (VDBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
-        return retVal;
-    }
-
-    /**
-     * Return next random number for the index
-     * @param index Retry index
-     */
-    private long nextRandomizationTime(int index) {
-        long randomTime = mRetryArray.get(index).mRandomizationTime;
-        if (randomTime == 0) {
-            return 0;
-        } else {
-            return mRng.nextInt((int) randomTime);
-        }
-    }
-
-    private long getNetworkSuggestedRetryDelay() {
-        long retryElapseTime = mDataThrottler.getRetryTime(apnType);
-        if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) {
-            return retryElapseTime;
-        }
-
-        // The time from data throttler is system's elapsed time. We need to return the delta. If
-        // less than 0, then return 0 (i.e. retry immediately).
-        return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime());
-    }
-
-    /**
-     * Get the next APN setting for data call setup.
-     * @return APN setting to try. {@code null} if cannot find any APN,
-     */
-    public @Nullable ApnSetting getNextApnSetting() {
-        if (mWaitingApns == null || mWaitingApns.size() == 0) {
-            log("Waiting APN list is null or empty.");
-            return null;
-        }
-
-        long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay();
-        if (networkSuggestedRetryDelay == NO_RETRY) {
-            log("Network suggested no retry.");
-            return null;
-        }
-
-        // If the network had suggested a retry delay, we should retry the current APN again
-        // (up to mMaxSameApnRetry times) instead of getting the next APN setting from
-        // our own list. If the APN waiting list has been reset before a setup data responses
-        // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry.
-        if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY
-                && mSameApnRetryCount < mMaxSameApnRetry) {
-            mSameApnRetryCount++;
-            return mWaitingApns.get(mCurrentApnIndex);
-        }
-
-        mSameApnRetryCount = 0;
-
-        int index = mCurrentApnIndex;
-        // Loop through the APN list to find out the index of next non-permanent failed APN.
-        while (true) {
-            if (++index == mWaitingApns.size()) index = 0;
-
-            // Stop if we find the non-failed APN.
-            if (!mWaitingApns.get(index).getPermanentFailed()) {
-                break;
-            }
-
-            // If all APNs have permanently failed, bail out.
-            if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) {
-                return null;
-            }
-        }
-
-        mCurrentApnIndex = index;
-        return mWaitingApns.get(mCurrentApnIndex);
-    }
-
-    /**
-     * Get the delay for trying the next waiting APN from the list.
-     * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
-     *                        delay.
-     * @return delay in milliseconds
-     */
-    public long getDelayForNextApn(boolean failFastEnabled) {
-
-        if (mWaitingApns == null || mWaitingApns.size() == 0) {
-            log("Waiting APN list is null or empty.");
-            return NO_RETRY;
-        }
-
-        long networkSuggestedDelay = getNetworkSuggestedRetryDelay();
-        log("Network suggested delay=" + networkSuggestedDelay + "ms");
-
-        if (networkSuggestedDelay == NO_RETRY) {
-            log("Network suggested not retrying.");
-            return NO_RETRY;
-        }
-
-        if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
-                && mSameApnRetryCount < mMaxSameApnRetry) {
-            // If the network explicitly suggests a retry delay, we should use it, even in fail fast
-            // mode.
-            log("Network suggested retry in " + networkSuggestedDelay + " ms.");
-            return networkSuggestedDelay;
-        }
-
-        // In order to determine the delay to try next APN, we need to peek the next available APN.
-        // Case 1 - If we will start the next round of APN trying,
-        //    we use the exponential-growth delay. (e.g. 5s, 10s, 30s...etc.)
-        // Case 2 - If we are still within the same round of APN trying,
-        //    we use the fixed standard delay between APNs. (e.g. 20s)
-
-        int index = mCurrentApnIndex;
-        while (true) {
-            if (++index >= mWaitingApns.size()) index = 0;
-
-            // Stop if we find the non-failed APN.
-            if (!mWaitingApns.get(index).getPermanentFailed()) {
-                break;
-            }
-
-            // If all APNs have permanently failed, bail out.
-            if (mWaitingApns.stream().allMatch(ApnSetting::getPermanentFailed)) {
-                log("All APNs have permanently failed.");
-                return NO_RETRY;
-            }
-        }
-
-        long delay;
-        if (index <= mCurrentApnIndex) {
-            // Case 1, if the next APN is in the next round.
-            if (!mRetryForever && mRetryCount + 1 > mMaxRetryCount) {
-                log("Reached maximum retry count " + mMaxRetryCount + ".");
-                return NO_RETRY;
-            }
-            delay = getRetryTimer();
-            ++mRetryCount;
-        } else {
-            // Case 2, if the next APN is still in the same round.
-            delay = mInterApnDelay;
-        }
-
-        if (failFastEnabled && delay > mFailFastInterApnDelay) {
-            // If we enable fail fast mode, and the delay we got is longer than
-            // fail-fast delay (mFailFastInterApnDelay), use the fail-fast delay.
-            // If the delay we calculated is already shorter than fail-fast delay,
-            // then ignore fail-fast delay.
-            delay = mFailFastInterApnDelay;
-        }
-
-        return delay;
-    }
-
-    /**
-     * Mark the APN setting permanently failed.
-     * @param apn APN setting to be marked as permanently failed
-     * */
-    public void markApnPermanentFailed(ApnSetting apn) {
-        if (apn != null) {
-            apn.setPermanentFailed(true);
-        }
-    }
-
-    /**
-     * Reset the retry manager.
-     */
-    private void reset() {
-        mMaxRetryCount = 0;
-        mRetryCount = 0;
-        mCurrentApnIndex = -1;
-        mSameApnRetryCount = 0;
-        mRetryArray.clear();
-    }
-
-    /**
-     * Set waiting APNs for retrying in case needed.
-     * @param waitingApns Waiting APN list
-     */
-    public void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
-
-        if (waitingApns == null) {
-            log("No waiting APNs provided");
-            return;
-        }
-
-        mWaitingApns = waitingApns;
-
-        // Since we replace the entire waiting APN list, we need to re-config this retry manager.
-        configureRetry();
-
-        for (ApnSetting apn : mWaitingApns) {
-            apn.setPermanentFailed(false);
-        }
-
-        log("Setting " + mWaitingApns.size() + " waiting APNs.");
-
-        if (VDBG) {
-            for (int i = 0; i < mWaitingApns.size(); i++) {
-                log("  [" + i + "]:" + mWaitingApns.get(i));
-            }
-        }
-    }
-
-    /**
-     * Get the list of waiting APNs.
-     * @return the list of waiting APNs
-     */
-    public @NonNull ArrayList<ApnSetting> getWaitingApns() {
-        return mWaitingApns;
-    }
-
-    /**
-     * Get the delay in milliseconds for APN retry after disconnect
-     * @return The delay in milliseconds
-     */
-    public long getRetryAfterDisconnectDelay() {
-        return mApnRetryAfterDisconnectDelay;
-    }
-
-    public String toString() {
-        if (mConfig == null) return "";
-        return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType)
-                + " mRetryCount="
-                + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex="
-                + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount
-                + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever="
-                + mRetryForever + " mInterApnDelay=" + mInterApnDelay
-                + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
-                + " mConfig={" + mConfig + "}";
-    }
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private void log(String s) {
-        Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 5bc9d17..1a444ff 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -27,12 +27,17 @@
 import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -80,6 +85,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.uicc.IccRecords;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -144,6 +150,23 @@
     protected static final int EVENT_ICC_CHANGED = 15;
     protected static final int EVENT_GET_IMS_SERVICE = 16;
 
+    /** Last TP - Message Reference value update to SIM */
+    private static final int EVENT_TPMR_SIM_UPDATE_RESPONSE = 17;
+
+    /** Handle SIM loaded  */
+    private static final int EVENT_SIM_LOADED = 18;
+
+    /**
+     * When this change is enabled, more specific values of SMS sending error code
+     * {@link SmsManager#Result} will be returned to the SMS Apps.
+     *
+     * Refer to {@link SMSDispatcher#rilErrorToSmsManagerResult} fore more details of the new values
+     * of SMS sending error code that will be returned.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    static final long ADD_MORE_SMS_SENDING_ERROR_CODES = 250017070L;
+
     @UnsupportedAppUsage
     protected Phone mPhone;
     @UnsupportedAppUsage
@@ -155,6 +178,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected final TelephonyManager mTelephonyManager;
     protected final LocalLog mLocalLog = new LocalLog(16);
+    protected final LocalLog mSmsOutgoingErrorCodes = new LocalLog(10);
 
     /** Maximum number of times to retry sending a failed SMS. */
     protected static final int MAX_SEND_RETRIES = 3;
@@ -190,6 +214,9 @@
     @VisibleForTesting
     public int mCarrierMessagingTimeout = 10 * 60 * 1000; //10 minutes
 
+    /** Used for storing last TP - Message Reference used*/
+    private int mMessageRef = -1;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected static int getNextConcatenatedRef() {
         sConcatenatedRef += 1;
@@ -215,10 +242,33 @@
                 com.android.internal.R.bool.config_sms_capable);
         mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone(
                 mPhone.getPhoneId(), mSmsCapable);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
+        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
         Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
                 + " mSmsSendDisabled=" + mSmsSendDisabled);
     }
 
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(final Context context, Intent intent) {
+            Rlog.d(TAG, "Received broadcast " + intent.getAction());
+            if (Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+                if (!intent.hasExtra(Intent.EXTRA_SIM_STATE)) {
+                    Rlog.d(TAG, "Extra not found in intent.");
+                } else {
+                    String simState = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
+                    if (simState.equals(Intent.SIM_STATE_LOADED)) {
+                        Rlog.d(TAG, "SIM_STATE_CHANGED : SIM_LOADED");
+                        Message msg = obtainMessage(EVENT_SIM_LOADED);
+                        msg.arg1 = getSubId();
+                        sendMessage(msg);
+                    }
+                }
+            }
+        }
+    };
+
     /**
      * Observe the secure setting for updated premium sms determination rules
      */
@@ -280,85 +330,153 @@
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-        case EVENT_SEND_SMS_COMPLETE:
-            // An outbound SMS has been successfully transferred, or failed.
-            handleSendComplete((AsyncResult) msg.obj);
-            break;
+            case EVENT_SEND_SMS_COMPLETE:
+                // An outbound SMS has been successfully transferred, or failed.
+                handleSendComplete((AsyncResult) msg.obj);
+                break;
 
-        case EVENT_SEND_RETRY:
-            Rlog.d(TAG, "SMS retry..");
-            sendRetrySms((SmsTracker) msg.obj);
-            break;
+            case EVENT_SEND_RETRY:
+                Rlog.d(TAG, "SMS retry..");
+                sendRetrySms((SmsTracker) msg.obj);
+                break;
 
-        case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
-            handleReachSentLimit((SmsTracker[]) (msg.obj));
-            break;
+            case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
+                handleReachSentLimit((SmsTracker[]) (msg.obj));
+                break;
 
-        case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
-            handleConfirmShortCode(false, (SmsTracker[]) (msg.obj));
-            break;
+            case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
+                handleConfirmShortCode(false, (SmsTracker[]) (msg.obj));
+                break;
 
-        case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
-            handleConfirmShortCode(true, (SmsTracker[]) (msg.obj));
-            break;
+            case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
+                handleConfirmShortCode(true, (SmsTracker[]) (msg.obj));
+                break;
 
-        case EVENT_SEND_CONFIRMED_SMS:
-        {
-            SmsTracker[] trackers = (SmsTracker[]) msg.obj;
-            for (SmsTracker tracker : trackers) {
-                sendSms(tracker);
-            }
-            mPendingTrackerCount--;
-            break;
-        }
-
-        case EVENT_SENDING_NOT_ALLOWED:
-        {
-            SmsTracker[] trackers = (SmsTracker[]) msg.obj;
-            Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
-                    + "sending SHORT_CODE_NEVER_ALLOWED error code.");
-            handleSmsTrackersFailure(
-                    trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
-            break;
-        }
-
-        case EVENT_STOP_SENDING:
-        {
-            SmsTracker[] trackers = (SmsTracker[]) msg.obj;
-            int error;
-            if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
-                if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
-                    error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
-                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
-                            + "sending SHORT_CODE_NEVER_ALLOWED error code.");
-                } else {
-                    error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
-                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
-                            + "sending SHORT_CODE_NOT_ALLOWED error code.");
+            case EVENT_SEND_CONFIRMED_SMS: {
+                SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+                for (SmsTracker tracker : trackers) {
+                    sendSms(tracker);
                 }
-            } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
-                error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
-                Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
-                        + "sending LIMIT_EXCEEDED error code.");
-            } else {
-                error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
-                Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+                mPendingTrackerCount--;
+                break;
             }
 
-            handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
-            mPendingTrackerCount--;
-            break;
-        }
+            case EVENT_SENDING_NOT_ALLOWED: {
+                SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+                Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
+                        + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+                handleSmsTrackersFailure(
+                        trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
+                break;
+            }
 
-        case EVENT_NEW_SMS_STATUS_REPORT:
-            handleStatusReport(msg.obj);
-            break;
+            case EVENT_STOP_SENDING: {
+                SmsTracker[] trackers = (SmsTracker[]) msg.obj;
+                int error;
+                if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
+                    if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
+                        error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+                        Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                                + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+                    } else {
+                        error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
+                        Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                                + "sending SHORT_CODE_NOT_ALLOWED error code.");
+                    }
+                } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
+                    error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
+                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                            + "sending LIMIT_EXCEEDED error code.");
+                } else {
+                    error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
+                    Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+                }
 
-        default:
-            Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
+                handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
+                mPendingTrackerCount--;
+                break;
+            }
+
+            case EVENT_NEW_SMS_STATUS_REPORT:
+                handleStatusReport(msg.obj);
+                break;
+            case EVENT_TPMR_SIM_UPDATE_RESPONSE:
+                handleMessageRefStatus(msg);
+                break;
+
+            case EVENT_SIM_LOADED:
+                /* sim TPMR value is given higher priority if both are non-negative number.
+                   Use case:
+                   if sim was used on another device and inserted in a new device,
+                   that device will start sending the next TPMR after reading from the SIM.
+                 */
+                mMessageRef = getTpmrValueFromSIM();
+                if (mMessageRef == -1) {
+                    mMessageRef = SubscriptionController.getInstance().getMessageRef(msg.arg1);
+                }
+                break;
+
+            default:
+                Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
         }
     }
 
+    private void handleMessageRefStatus(Message msg) {
+        AsyncResult ar = (AsyncResult) msg.obj;
+        if (ar.exception != null) {
+            Rlog.e(TAG, "Failed to update TP - Message reference value to SIM " + ar.exception);
+        } else {
+            Rlog.d(TAG, "TP - Message reference updated to SIM Successfully");
+        }
+    }
+
+    private void updateTPMessageReference() {
+        updateSIMLastTPMRValue(mMessageRef);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            SubscriptionController.getInstance().updateMessageRef(getSubId(), mMessageRef);
+        } catch (SecurityException e) {
+            Rlog.e(TAG, "Security Exception caused on messageRef updation to DB " + e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void updateSIMLastTPMRValue(int messageRef) {
+        Message msg = obtainMessage(EVENT_TPMR_SIM_UPDATE_RESPONSE);
+        IccRecords iccRecords = getIccRecords();
+        if (iccRecords != null) {
+            iccRecords.setSmssTpmrValue(messageRef, msg);
+        }
+    }
+
+    private int getTpmrValueFromSIM() {
+        IccRecords iccRecords = getIccRecords();
+        if (iccRecords != null) {
+            return iccRecords.getSmssTpmrValue();
+        }
+        return -1;
+    }
+
+    private IccRecords getIccRecords() {
+        if (mPhone != null && mPhone.getIccRecords() != null) {
+            return mPhone.getIccRecords();
+        }
+        return null;
+    }
+
+    /**
+     *  Returns the next TP message Reference value incremented by 1 for every sms sent .
+     *  once a max of 255 is reached TP message Reference is reset to 0.
+     *
+     *  @return messageRef TP message Reference value
+     */
+    public int nextMessageRef() {
+        mMessageRef = (mMessageRef + 1) % 256;
+        updateTPMessageReference();
+        return mMessageRef;
+    }
+
     /**
      * Use the carrier messaging service to send a data or text SMS.
      */
@@ -569,26 +687,50 @@
         @Override
         public void onSendSmsComplete(int result, int messageRef) {
             Rlog.d(TAG, "onSendSmsComplete: result=" + result + " messageRef=" + messageRef);
-            if (mCallbackCalled) {
-                logWithLocalLog("onSendSmsComplete: unexpected call");
-                AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
-                        "Unexpected onSendSmsComplete", mPhone.getCarrierId());
+            if (cleanupOnSendSmsComplete("onSendSmsComplete")) {
                 return;
             }
-            mCallbackCalled = true;
+
             final long identity = Binder.clearCallingIdentity();
             try {
-                mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
                 processSendSmsResponse(mSmsSender.getSmsTracker(), result, messageRef);
-                mSmsSender.removeTimeout();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
+        /**
+         * This method should be called only once.
+         */
         @Override
         public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
-            Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with result: " + result);
+            Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs="
+                    + Arrays.toString(messageRefs));
+            if (cleanupOnSendSmsComplete("onSendMultipartSmsComplete")) {
+                return;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                processSendMultipartSmsResponse(mSmsSender.getSmsTrackers(), result, messageRefs);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        private boolean cleanupOnSendSmsComplete(String callingFunction) {
+            if (mCallbackCalled) {
+                logWithLocalLog(callingFunction + ": unexpected call");
+                AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
+                        "Unexpected " + callingFunction, mPhone.getCarrierId());
+                return true;
+            }
+
+            mCallbackCalled = true;
+            mSmsSender.removeTimeout();
+            mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
+
+            return false;
         }
 
         @Override
@@ -663,8 +805,7 @@
         }
 
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        void sendSmsByCarrierApp(String carrierPackageName,
-                                 MultipartSmsSenderCallback senderCallback) {
+        void sendSmsByCarrierApp(String carrierPackageName, SmsSenderCallback senderCallback) {
             super.sendSmsByCarrierApp(carrierPackageName, senderCallback);
         }
 
@@ -712,69 +853,6 @@
         }
     }
 
-    /**
-     * Callback for MultipartSmsSender from the carrier messaging service.
-     * Once the result is ready, the carrier messaging service connection is disposed.
-     */
-    private final class MultipartSmsSenderCallback implements CarrierMessagingCallback {
-        private final MultipartSmsSender mSmsSender;
-        private boolean mCallbackCalled = false;
-
-        MultipartSmsSenderCallback(MultipartSmsSender smsSender) {
-            mSmsSender = smsSender;
-        }
-
-        @Override
-        public void onSendSmsComplete(int result, int messageRef) {
-            Rlog.e(TAG, "Unexpected onSendSmsComplete call with result: " + result);
-        }
-
-        /**
-         * This method should be called only once.
-         */
-        @Override
-        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
-            Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs="
-                    + Arrays.toString(messageRefs));
-            if (mCallbackCalled) {
-                logWithLocalLog("onSendMultipartSmsComplete: unexpected call");
-                AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
-                        "Unexpected onSendMultipartSmsComplete", mPhone.getCarrierId());
-                return;
-            }
-            mCallbackCalled = true;
-            mSmsSender.removeTimeout();
-            mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
-
-            if (mSmsSender.mTrackers == null) {
-                Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers.");
-                return;
-            }
-
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                processSendMultipartSmsResponse(mSmsSender.mTrackers, result, messageRefs);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public void onReceiveSmsComplete(int result) {
-            Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
-        }
-
-        @Override
-        public void onSendMmsComplete(int result, byte[] sendConfPdu) {
-            Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result);
-        }
-
-        @Override
-        public void onDownloadMmsComplete(int result) {
-            Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result);
-        }
-    }
-
     private void processSendMultipartSmsResponse(
             SmsTracker[] trackers, int result, int[] messageRefs) {
         if (trackers == null) {
@@ -981,8 +1059,31 @@
     }
 
     @SmsManager.Result
-    private static int rilErrorToSmsManagerResult(CommandException.Error rilError,
+    private int rilErrorToSmsManagerResult(CommandException.Error rilError,
             SmsTracker tracker) {
+        mSmsOutgoingErrorCodes.log("rilError: " + rilError
+                + ", MessageId: " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
+
+        ApplicationInfo appInfo = tracker.getAppInfo();
+        if (appInfo == null
+                || !CompatChanges.isChangeEnabled(ADD_MORE_SMS_SENDING_ERROR_CODES, appInfo.uid)) {
+            if (rilError == CommandException.Error.INVALID_RESPONSE
+                    || rilError == CommandException.Error.SIM_PIN2
+                    || rilError == CommandException.Error.SIM_PUK2
+                    || rilError == CommandException.Error.SUBSCRIPTION_NOT_AVAILABLE
+                    || rilError == CommandException.Error.SIM_ERR
+                    || rilError == CommandException.Error.INVALID_SIM_STATE
+                    || rilError == CommandException.Error.NO_SMS_TO_ACK
+                    || rilError == CommandException.Error.SIM_BUSY
+                    || rilError == CommandException.Error.SIM_FULL
+                    || rilError == CommandException.Error.NO_SUBSCRIPTION
+                    || rilError == CommandException.Error.NO_NETWORK_FOUND
+                    || rilError == CommandException.Error.DEVICE_IN_USE
+                    || rilError == CommandException.Error.ABORTED) {
+                return SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+            }
+        }
+
         switch (rilError) {
             case RADIO_NOT_AVAILABLE:
                 return SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE;
@@ -1032,6 +1133,34 @@
                 return SmsManager.RESULT_RIL_ACCESS_BARRED;
             case BLOCKED_DUE_TO_CALL:
                 return SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL;
+            case INVALID_SMSC_ADDRESS:
+                return SmsManager.RESULT_INVALID_SMSC_ADDRESS;
+            case INVALID_RESPONSE:
+                return SmsManager.RESULT_RIL_INVALID_RESPONSE;
+            case SIM_PIN2:
+                return SmsManager.RESULT_RIL_SIM_PIN2;
+            case SIM_PUK2:
+                return SmsManager.RESULT_RIL_SIM_PUK2;
+            case SUBSCRIPTION_NOT_AVAILABLE:
+                return SmsManager.RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE;
+            case SIM_ERR:
+                return SmsManager.RESULT_RIL_SIM_ERROR;
+            case INVALID_SIM_STATE:
+                return SmsManager.RESULT_RIL_INVALID_SIM_STATE;
+            case NO_SMS_TO_ACK:
+                return SmsManager.RESULT_RIL_NO_SMS_TO_ACK;
+            case SIM_BUSY:
+                return SmsManager.RESULT_RIL_SIM_BUSY;
+            case SIM_FULL:
+                return SmsManager.RESULT_RIL_SIM_FULL;
+            case NO_SUBSCRIPTION:
+                return SmsManager.RESULT_RIL_NO_SUBSCRIPTION;
+            case NO_NETWORK_FOUND:
+                return SmsManager.RESULT_RIL_NO_NETWORK_FOUND;
+            case DEVICE_IN_USE:
+                return SmsManager.RESULT_RIL_DEVICE_IN_USE;
+            case ABORTED:
+                return SmsManager.RESULT_RIL_ABORTED;
             default:
                 Rlog.d(TAG, "rilErrorToSmsManagerResult: " + rilError + " "
                         + SmsController.formatCrossStackMessageId(tracker.mMessageId));
@@ -1133,14 +1262,15 @@
     @UnsupportedAppUsage
     protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+        int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
-                scAddr, destAddr, destPort, data, (deliveryIntent != null));
+                scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef);
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
             SmsTracker tracker = getSmsTracker(callingPackage, map, sentIntent, deliveryIntent,
                     getFormat(), null /*messageUri*/, false /*expectMore*/,
                     null /*fullMessageText*/, false /*isText*/,
-                    true /*persistMessage*/, isForVvm, 0L /* messageId */);
+                    true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef);
 
             if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) {
                 sendSubmitPdu(tracker);
@@ -1252,18 +1382,131 @@
      *                 Used for logging and diagnostics purposes. The id may be NULL.
      */
     public void sendText(String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
+            String callingPkg, boolean persistMessage, int priority,
+            boolean expectMore, int validityPeriod, boolean isForVvm,
+            long messageId) {
+        sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
+                persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+    }
+
+    /**
+     * Send a text based SMS.
+     *
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:<br>
+     *  <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+     *  <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>SmsManager.RESULT_INVALID_STATE</code><br>
+     *  <code>SmsManager.RESULT_NO_MEMORY</code><br>
+     *  <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+     *  <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+     *  <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+     *  <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+     *  <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+     *  <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+     *  <code>SmsManager.RESULT_CANCELLED</code><br>
+     *  <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+     *  <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     * @param messageUri optional URI of the message if it is already stored in the system
+     * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *  non-default SMS app.
+     *
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     * @param messageId An id that uniquely identifies the message requested to be sent.
+     *                 Used for logging and diagnostics purposes. The id may be NULL.
+     * @param skipShortCodeCheck Skip check for short code type destination address.
+     */
+    public void sendText(String destAddr, String scAddr, String text,
                          PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
                          String callingPkg, boolean persistMessage, int priority,
                          boolean expectMore, int validityPeriod, boolean isForVvm,
-                         long messageId) {
+                         long messageId, boolean skipShortCodeCheck) {
         Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId));
+        int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
-                scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod);
+                scAddr, destAddr, text, (deliveryIntent != null), null, priority, validityPeriod,
+                messageRef);
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
             SmsTracker tracker = getSmsTracker(callingPkg, map, sentIntent, deliveryIntent,
                     getFormat(), messageUri, expectMore, text, true /*isText*/,
-                    persistMessage, priority, validityPeriod, isForVvm, messageId);
+                    persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef,
+                    skipShortCodeCheck);
 
             if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
                 sendSubmitPdu(tracker);
@@ -1319,6 +1562,14 @@
     protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
             int destPort, byte[] message, boolean statusReportRequested);
 
+    protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader,
+            int priority, int validityPeriod, int messageRef);
+
+
+    protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef);
+
     /**
      * Calculate the number of septets needed to encode the message. This function should only be
      * called for individual segments of multipart message.
@@ -1494,12 +1745,13 @@
             if (deliveryIntents != null && deliveryIntents.size() > i) {
                 deliveryIntent = deliveryIntents.get(i);
             }
-
+            int messageRef = nextMessageRef();
             trackers[i] =
                 getNewSubmitPduTracker(callingPkg, destAddr, scAddr, parts.get(i), smsHeader,
                         encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)),
                         unsentPartCount, anyPartFailed, messageUri,
-                        fullMessageText, priority, expectMore, validityPeriod, messageId);
+                        fullMessageText, priority, expectMore, validityPeriod, messageId,
+                        messageRef);
             if (trackers[i] == null) {
                 triggerSentIntentForFailure(sentIntents);
                 return;
@@ -1509,12 +1761,10 @@
 
         String carrierPackage = getCarrierAppPackageName();
         if (carrierPackage != null) {
-            Rlog.d(TAG, "Found carrier package " + carrierPackage
-                    + " "
+            Rlog.d(TAG, "Found carrier package " + carrierPackage + " "
                     + SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers)));
             MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
-            smsSender.sendSmsByCarrierApp(carrierPackage,
-                    new MultipartSmsSenderCallback(smsSender));
+            smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
         } else {
             Rlog.v(TAG, "No carrier package. "
                     + SmsController.formatCrossStackMessageId(getMultiTrackermessageId(trackers)));
@@ -1537,7 +1787,7 @@
             PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             String fullMessageText, int priority, boolean expectMore, int validityPeriod,
-            long messageId) {
+            long messageId, int messageRef) {
         if (isCdmaMo()) {
             UserData uData = new UserData();
             uData.payloadStr = message;
@@ -1567,7 +1817,7 @@
                         getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
                         (!lastPart || expectMore), fullMessageText, true /*isText*/,
                         true /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
-                        messageId);
+                        messageId, messageRef, false);
             } else {
                 Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1578,15 +1828,14 @@
                     com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
                             destinationAddress, message, deliveryIntent != null,
                             SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable,
-                            smsHeader.languageShiftTable, validityPeriod);
+                            smsHeader.languageShiftTable, validityPeriod, messageRef);
             if (pdu != null) {
-                HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
-                        message, pdu);
+                HashMap map = getSmsTrackerMap(destinationAddress, scAddress, message, pdu);
                 return getSmsTracker(callingPackage, map, sentIntent,
                         deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
                         smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/,
                         false /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
-                        messageId);
+                        messageId, messageRef, false);
             } else {
                 Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1744,7 +1993,8 @@
      */
     boolean checkDestination(SmsTracker[] trackers) {
         if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION)
-                == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm) {
+                == PackageManager.PERMISSION_GRANTED || trackers[0].mIsForVvm
+                || trackers[0].mSkipShortCodeDestAddrCheck) {
             return true;            // app is pre-approved to send to short codes
         } else {
             int rule = mPremiumSmsRule.get();
@@ -1785,6 +2035,12 @@
                                                 trackers[0].mDestAddress, networkCountryIso));
             }
 
+            if (smsCategory != SmsManager.SMS_CATEGORY_NOT_SHORT_CODE) {
+                int xmlVersion = mSmsDispatchersController.getUsageMonitor()
+                        .getShortCodeXmlFileVersion();
+                mPhone.getSmsStats().onOutgoingShortCodeSms(smsCategory, xmlVersion);
+            }
+
             if (smsCategory == SmsManager.SMS_CATEGORY_NOT_SHORT_CODE
                     || smsCategory == SmsManager.SMS_CATEGORY_FREE_SHORT_CODE
                     || smsCategory == SmsManager.SMS_CATEGORY_STANDARD_SHORT_CODE) {
@@ -2082,6 +2338,7 @@
         private Boolean mIsFromDefaultSmsApplication;
 
         private int mCarrierId;
+        private boolean mSkipShortCodeDestAddrCheck;
         // SMS anomaly uuid -- unexpected error from RIL
         private final UUID mAnomalyUnexpectedErrorFromRilUUID =
                 UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886");
@@ -2091,7 +2348,8 @@
                 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
                 SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId,
                 boolean isText, boolean persistMessage, int userId, int priority,
-                int validityPeriod, boolean isForVvm, long messageId, int carrierId) {
+                int validityPeriod, boolean isForVvm, long messageId, int carrierId,
+                int messageRef, boolean skipShortCodeDestAddrCheck) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
@@ -2102,7 +2360,7 @@
             mExpectMore = expectMore;
             mImsRetry = 0;
             mUsesImsServiceForIms = false;
-            mMessageRef = 0;
+            mMessageRef = messageRef;
             mUnsentPartCount = unsentPartCount;
             mAnyPartFailed = anyPartFailed;
             mMessageUri = messageUri;
@@ -2117,6 +2375,7 @@
             mIsForVvm = isForVvm;
             mMessageId = messageId;
             mCarrierId = carrierId;
+            mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck;
         }
 
         public HashMap<String, Object> getData() {
@@ -2131,6 +2390,14 @@
             return mAppInfo != null ? mAppInfo.packageName : null;
         }
 
+        /**
+         * Get the calling Application Info
+         * @return Application Info
+         */
+        public ApplicationInfo getAppInfo() {
+            return mAppInfo == null ? null : mAppInfo.applicationInfo;
+        }
+
         /** Return if the SMS was originated from the default SMS application. */
         public boolean isFromDefaultSmsApplication(Context context) {
             if (mIsFromDefaultSmsApplication == null) {
@@ -2384,7 +2651,7 @@
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText,
             boolean persistMessage, int priority, int validityPeriod, boolean isForVvm,
-            long messageId) {
+            long messageId, int messageRef, boolean skipShortCodeCheck) {
         // Get package info via packagemanager
         UserHandle callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid());
         final int userId = callingUser.getIdentifier();
@@ -2401,28 +2668,30 @@
         return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
                 unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore,
                 fullMessageText, getSubId(), isText, persistMessage, userId, priority,
-                validityPeriod, isForVvm, messageId, mPhone.getCarrierId());
+                validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef,
+                skipShortCodeCheck);
     }
 
     protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
             PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
             boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
-            boolean isForVvm, long messageId) {
+            boolean isForVvm, long messageId, int messageRef) {
         return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
                 null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
                 expectMore, fullMessageText, isText, persistMessage,
                 SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
-                messageId);
+                messageId, messageRef, false);
     }
 
     protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
             PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
             boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
-            int priority, int validityPeriod, boolean isForVvm, long messageId) {
+            int priority, int validityPeriod, boolean isForVvm, long messageId, int messageRef,
+            boolean skipShortCodeCheck) {
         return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
                 null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
                 expectMore, fullMessageText, isText, persistMessage, priority, validityPeriod,
-                isForVvm, messageId);
+                isForVvm, messageId, messageRef, skipShortCodeCheck);
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
@@ -2641,10 +2910,17 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println(TAG);
         pw.increaseIndent();
+
         pw.println("mLocalLog:");
         pw.increaseIndent();
         mLocalLog.dump(fd, pw, args);
         pw.decreaseIndent();
+
+        pw.println("mSmsOutgoingErrorCodes:");
+        pw.increaseIndent();
+        mSmsOutgoingErrorCodes.dump(fd, pw, args);
+        pw.decreaseIndent();
+
         pw.decreaseIndent();
     }
 }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 0f2991b..31716ff 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -37,7 +37,6 @@
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.hardware.radio.V1_0.CellInfoType;
-import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
 import android.os.BaseBundle;
 import android.os.Build;
@@ -95,7 +94,6 @@
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.DataNetwork;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
-import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.metrics.ServiceStateStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -218,13 +216,11 @@
     private RegistrantList mNrStateChangedRegistrants = new RegistrantList();
     private RegistrantList mNrFrequencyChangedRegistrants = new RegistrantList();
     private RegistrantList mCssIndicatorChangedRegistrants = new RegistrantList();
-    private final RegistrantList mBandwidthChangedRegistrants = new RegistrantList();
     private final RegistrantList mAirplaneModeChangedRegistrants = new RegistrantList();
     private final RegistrantList mAreaCodeChangedRegistrants = new RegistrantList();
 
     /* Radio power off pending flag and tag counter */
     private boolean mPendingRadioPowerOffAfterDataOff = false;
-    private int mPendingRadioPowerOffAfterDataOffTag = 0;
 
     /** Waiting period before recheck gprs and voice registration. */
     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
@@ -323,8 +319,6 @@
     private CarrierDisplayNameResolver mCdnr;
 
     private boolean mImsRegistrationOnOff = false;
-    /** Radio is disabled by carrier. Radio power will not be override if this field is set */
-    private boolean mRadioDisabledByCarrier = false;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private boolean mDeviceShuttingDown = false;
     /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
@@ -686,7 +680,7 @@
                 Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
         mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0);
         if (!mDesiredPowerState) {
-            mRadioPowerOffReasons.add(Phone.RADIO_POWER_REASON_USER);
+            mRadioPowerOffReasons.add(TelephonyManager.RADIO_POWER_REASON_USER);
         }
         mRadioPowerLog.log("init : airplane mode = " + airplaneMode + " enableCellularOnBoot = " +
                 enableCellularOnBoot);
@@ -721,17 +715,15 @@
         registerForImsCapabilityChanged(mCSST,
                 CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
 
-        if (mPhone.isUsingNewDataStack()) {
-            mDataDisconnectedCallback = new DataNetworkControllerCallback(this::post) {
-                @Override
-                public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) {
-                    log("onAnyDataNetworkExistingChanged: anyDataExisting=" + anyDataExisting);
-                    if (!anyDataExisting) {
-                        sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED);
-                    }
+        mDataDisconnectedCallback = new DataNetworkControllerCallback(this::post) {
+            @Override
+            public void onAnyDataNetworkExistingChanged(boolean anyDataExisting) {
+                log("onAnyDataNetworkExistingChanged: anyDataExisting=" + anyDataExisting);
+                if (!anyDataExisting) {
+                    sendEmptyMessage(EVENT_ALL_DATA_DISCONNECTED);
                 }
-            };
-        }
+            }
+        };
     }
 
     @VisibleForTesting
@@ -872,7 +864,10 @@
     public boolean getDesiredPowerState() {
         return mDesiredPowerState;
     }
-    public boolean getPowerStateFromCarrier() { return !mRadioDisabledByCarrier; }
+
+    public boolean getPowerStateFromCarrier() {
+        return !mRadioPowerOffReasons.contains(TelephonyManager.RADIO_POWER_REASON_CARRIER);
+    }
 
     public List<PhysicalChannelConfig> getPhysicalChannelConfigList() {
         return mLastPhysicalChannelConfigList;
@@ -1062,7 +1057,7 @@
      * @return the current reasons for which the radio is off.
      */
     public Set<Integer> getRadioPowerOffReasons() {
-        return mRadioPowerOffReasons;
+        return Set.copyOf(mRadioPowerOffReasons);
     }
 
     /**
@@ -1088,7 +1083,7 @@
     public void setRadioPower(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
-                Phone.RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     /**
@@ -1129,23 +1124,6 @@
     }
 
     /**
-     * Radio power set from carrier action. if set to false means carrier desire to turn radio off
-     * and radio wont be re-enabled unless carrier explicitly turn it back on.
-     * @param enable indicate if radio power is enabled or disabled from carrier action.
-     */
-    public void setRadioPowerFromCarrier(boolean enable) {
-        boolean disableByCarrier = !enable;
-        if (mRadioDisabledByCarrier == disableByCarrier) {
-            log("setRadioPowerFromCarrier mRadioDisabledByCarrier is already "
-                    + disableByCarrier + " Do nothing.");
-            return;
-        }
-
-        mRadioDisabledByCarrier = disableByCarrier;
-        setPowerStateToDesired();
-    }
-
-    /**
      * These two flags manage the behavior of the cell lock -- the
      * lock should be held if either flag is true.  The intention is
      * to allow temporary acquisition of the lock to get a single
@@ -1197,22 +1175,9 @@
         switch (msg.what) {
             case EVENT_SET_RADIO_POWER_OFF:
                 synchronized(this) {
-                    if (mPhone.isUsingNewDataStack()) {
-                        mPendingRadioPowerOffAfterDataOff = false;
-                        log("Wait for all data networks torn down timed out. Power off now.");
-                        hangupAndPowerOff();
-                        return;
-                    }
-                    if (mPendingRadioPowerOffAfterDataOff &&
-                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
-                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
-                        hangupAndPowerOff();
-                        mPendingRadioPowerOffAfterDataOffTag += 1;
-                        mPendingRadioPowerOffAfterDataOff = false;
-                    } else {
-                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
-                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
-                    }
+                    mPendingRadioPowerOffAfterDataOff = false;
+                    log("Wait for all data networks torn down timed out. Power off now.");
+                    hangupAndPowerOff();
                 }
                 break;
 
@@ -1480,41 +1445,26 @@
                 break;
 
             case EVENT_ALL_DATA_DISCONNECTED:
-                if (mPhone.isUsingNewDataStack()) {
-                    log("EVENT_ALL_DATA_DISCONNECTED");
-                    if (!mPendingRadioPowerOffAfterDataOff) return;
-                    boolean areAllDataDisconnectedOnAllPhones = true;
-                    for (Phone phone : PhoneFactory.getPhones()) {
-                        if (phone.getDataNetworkController().areAllDataDisconnected()) {
-                            phone.getDataNetworkController()
-                                    .unregisterDataNetworkControllerCallback(
-                                            mDataDisconnectedCallback);
-                        } else {
-                            log("Still waiting for all data disconnected on phone: "
-                                    + phone.getSubId());
-                            areAllDataDisconnectedOnAllPhones = false;
-                        }
-                    }
-                    if (areAllDataDisconnectedOnAllPhones) {
-                        mPendingRadioPowerOffAfterDataOff = false;
-                        removeMessages(EVENT_SET_RADIO_POWER_OFF);
-                        if (DBG) log("Data disconnected for all phones, turn radio off now.");
-                        hangupAndPowerOff();
-                    }
-                    return;
-                }
-                int dds = SubscriptionManager.getDefaultDataSubscriptionId();
-                ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
-                synchronized(this) {
-                    if (mPendingRadioPowerOffAfterDataOff) {
-                        if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
-                        hangupAndPowerOff();
-                        mPendingRadioPowerOffAfterDataOffTag += 1;
-                        mPendingRadioPowerOffAfterDataOff = false;
+                log("EVENT_ALL_DATA_DISCONNECTED");
+                if (!mPendingRadioPowerOffAfterDataOff) return;
+                boolean areAllDataDisconnectedOnAllPhones = true;
+                for (Phone phone : PhoneFactory.getPhones()) {
+                    if (phone.getDataNetworkController().areAllDataDisconnected()) {
+                        phone.getDataNetworkController()
+                                .unregisterDataNetworkControllerCallback(
+                                        mDataDisconnectedCallback);
                     } else {
-                        log("EVENT_ALL_DATA_DISCONNECTED is stale");
+                        log("Still waiting for all data disconnected on phone: "
+                                + phone.getSubId());
+                        areAllDataDisconnectedOnAllPhones = false;
                     }
                 }
+                if (areAllDataDisconnectedOnAllPhones) {
+                    mPendingRadioPowerOffAfterDataOff = false;
+                    removeMessages(EVENT_SET_RADIO_POWER_OFF);
+                    if (DBG) log("Data disconnected for all phones, turn radio off now.");
+                    hangupAndPowerOff();
+                }
                 break;
 
             case EVENT_CHANGE_IMS_STATE:
@@ -1670,7 +1620,8 @@
                 if (ar.exception == null) {
                     boolean enable = (boolean) ar.result;
                     if (DBG) log("EVENT_RADIO_POWER_FROM_CARRIER: " + enable);
-                    setRadioPowerFromCarrier(enable);
+                    setRadioPowerForReason(enable, false, false, false,
+                            TelephonyManager.RADIO_POWER_REASON_CARRIER);
                 }
                 break;
 
@@ -1699,6 +1650,7 @@
                     // Notify NR frequency, NR connection status or bandwidths changed.
                     if (hasChanged) {
                         mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+                        mServiceStateChangedRegistrants.notifyRegistrants();
                         TelephonyMetrics.getInstance().writeServiceStateChanged(
                                 mPhone.getPhoneId(), mSS);
                         mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
@@ -2114,16 +2066,20 @@
         if (physicalChannelConfigs != null) {
             for (PhysicalChannelConfig config : physicalChannelConfigs) {
                 if (isNrPhysicalChannelConfig(config) && isInternetPhysicalChannelConfig(config)) {
-                    // Update the NR frequency range if there is an internet data connection
+                    // Update the NR frequency range if there is an active internet data connection
                     // associated with this NR physical channel channel config.
-                    newFrequencyRange = ServiceState.getBetterNRFrequencyRange(
-                            newFrequencyRange, config.getFrequencyRange());
-                    break;
+                    // If there are multiple valid configs, use the highest frequency range value.
+                    newFrequencyRange = Math.max(newFrequencyRange, config.getFrequencyRange());
                 }
             }
         }
 
         boolean hasChanged = newFrequencyRange != ss.getNrFrequencyRange();
+        if (hasChanged) {
+            log(String.format("NR frequency range changed from %s to %s.",
+                    ServiceState.frequencyRangeToString(ss.getNrFrequencyRange()),
+                    ServiceState.frequencyRangeToString(newFrequencyRange)));
+        }
         ss.setNrFrequencyRange(newFrequencyRange);
         return hasChanged;
     }
@@ -2154,6 +2110,11 @@
         }
 
         boolean hasChanged = newNrState != oldNrState;
+        if (hasChanged) {
+            log(String.format("NR state changed from %s to %s.",
+                    NetworkRegistrationInfo.nrStateToString(oldNrState),
+                    NetworkRegistrationInfo.nrStateToString(newNrState)));
+        }
         regInfo.setNrState(newNrState);
         ss.addNetworkRegistrationInfo(regInfo);
         return hasChanged;
@@ -2165,18 +2126,8 @@
 
     private boolean isInternetPhysicalChannelConfig(PhysicalChannelConfig config) {
         for (int cid : config.getContextIds()) {
-            if (mPhone.isUsingNewDataStack()) {
-                if (mPhone.getDataNetworkController().isInternetNetwork(cid)) {
-                    return true;
-                }
-            } else {
-                DataConnection dc = mPhone.getDcTracker(
-                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .getDataConnectionByContextId(cid);
-                if (dc != null && dc.getNetworkCapabilities().hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-                    return true;
-                }
+            if (mPhone.getDataNetworkController().isInternetNetwork(cid)) {
+                return true;
             }
         }
         return false;
@@ -3114,12 +3065,12 @@
     protected void setPowerStateToDesired(boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         if (DBG) {
-            String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown +
-                    ", mDesiredPowerState=" + mDesiredPowerState +
-                    ", getRadioState=" + mCi.getRadioState() +
-                    ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier +
-                    ", IMS reg state=" + mImsRegistrationOnOff +
-                    ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
+            String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown
+                    + ", mDesiredPowerState=" + mDesiredPowerState
+                    + ", getRadioState=" + mCi.getRadioState()
+                    + ", mRadioPowerOffReasons=" + mRadioPowerOffReasons
+                    + ", IMS reg state=" + mImsRegistrationOnOff
+                    + ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
             log(tmpLog);
             mRadioPowerLog.log(tmpLog);
         }
@@ -3131,22 +3082,13 @@
         }
 
         // If we want it on and it's off, turn it on
-        if (mDesiredPowerState && !mRadioDisabledByCarrier
+        if (mDesiredPowerState && mRadioPowerOffReasons.isEmpty()
                 && (forceApply || mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF)) {
             mCi.setRadioPower(true, forEmergencyCall, isSelectedPhoneForEmergencyCall, null);
-        } else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState()
+        } else if ((!mDesiredPowerState || !mRadioPowerOffReasons.isEmpty()) && mCi.getRadioState()
                 == TelephonyManager.RADIO_POWER_ON) {
-            // If it's on and available and we want it off gracefully
-            if (!mPhone.isUsingNewDataStack() && mImsRegistrationOnOff
-                    && getRadioPowerOffDelayTimeoutForImsRegistration() > 0) {
-                if (DBG) log("setPowerStateToDesired: delaying power off until IMS dereg.");
-                startDelayRadioOffWaitingForImsDeregTimeout();
-                // Return early here as we do not want to hit the cancel timeout code below.
-                return;
-            } else {
-                if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()");
-                powerOffRadioSafely();
-            }
+            if (DBG) log("setPowerStateToDesired: powerOffRadioSafely()");
+            powerOffRadioSafely();
         } else if (mDeviceShuttingDown
                 && (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE)) {
             // !mDesiredPowerState condition above will happen first if the radio is on, so we will
@@ -3306,7 +3248,6 @@
                 + " mImsRegistrationOnOff=" + mImsRegistrationOnOff
                 + "}");
 
-
         if (mImsRegistrationOnOff && !registered) {
             // moving to deregistered, only send this event if we need to re-evaluate
             if (getRadioPowerOffDelayTimeoutForImsRegistration() > 0) {
@@ -3319,6 +3260,9 @@
             }
         }
         mImsRegistrationOnOff = registered;
+
+        // It's possible ServiceState changes did not trigger SPN display update; we update it here.
+        updateSpnDisplay();
     }
 
     public void onImsCapabilityChanged() {
@@ -3467,8 +3411,8 @@
         updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
         updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
 
-        if (TelephonyUtils.IS_DEBUGGABLE && mPhone.mTelephonyTester != null) {
-            mPhone.mTelephonyTester.overrideServiceState(mNewSS);
+        if (TelephonyUtils.IS_DEBUGGABLE && mPhone.getTelephonyTester() != null) {
+            mPhone.getTelephonyTester().overrideServiceState(mNewSS);
         }
 
         NetworkRegistrationInfo networkRegState = mNewSS.getNetworkRegistrationInfo(
@@ -3520,11 +3464,6 @@
             NetworkRegistrationInfo newNrs = mNewSS.getNetworkRegistrationInfo(
                     NetworkRegistrationInfo.DOMAIN_PS, transport);
 
-            // If the previously it was not in service, and now it's in service, trigger the
-            // attached event. Also if airplane mode was just turned on, and data is already in
-            // service, we need to trigger the attached event again so that DcTracker can setup
-            // data on all connectable APNs again (because we've already torn down all data
-            // connections just before airplane mode turned on)
             boolean changed = (oldNrs == null || !oldNrs.isInService() || hasAirplaneModeOnChanged)
                     && (newNrs != null && newNrs.isInService());
             hasDataAttached.put(transport, changed);
@@ -3746,10 +3685,6 @@
             mCssIndicatorChangedRegistrants.notifyRegistrants();
         }
 
-        if (hasBandwidthChanged) {
-            mBandwidthChangedRegistrants.notifyRegistrants();
-        }
-
         if (hasRejectCauseChanged) {
             setNotification(CS_REJECT_CAUSE_ENABLED);
         }
@@ -5003,9 +4938,10 @@
      *
      * @param h handler to notify
      * @param what what code of message when delivered
+     * @param userobj the user obj that will be passed back when notify
      */
-    public void registerForServiceStateChanged(Handler h, int what) {
-        mServiceStateChangedRegistrants.addUnique(h, what, null);
+    public void registerForServiceStateChanged(Handler h, int what, Object userobj) {
+        mServiceStateChangedRegistrants.addUnique(h, what, userobj);
     }
 
     /**
@@ -5025,96 +4961,33 @@
     public void powerOffRadioSafely() {
         synchronized (this) {
             if (!mPendingRadioPowerOffAfterDataOff) {
-                if (mPhone.isUsingNewDataStack()) {
-                    // hang up all active voice calls first
-                    if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
-                        mPhone.mCT.mRingingCall.hangupIfAlive();
-                        mPhone.mCT.mBackgroundCall.hangupIfAlive();
-                        mPhone.mCT.mForegroundCall.hangupIfAlive();
-                    }
-
-                    for (Phone phone : PhoneFactory.getPhones()) {
-                        if (!phone.getDataNetworkController().areAllDataDisconnected()) {
-                            log("powerOffRadioSafely: Data is active on phone " + phone.getSubId()
-                                    + ". Wait for all data disconnect.");
-                            mPendingRadioPowerOffAfterDataOff = true;
-                            phone.getDataNetworkController().registerDataNetworkControllerCallback(
-                                    mDataDisconnectedCallback);
-                        }
-                    }
-
-                    // Tear down outside of the disconnected check to prevent race conditions.
-                    mPhone.getDataNetworkController().tearDownAllDataNetworks(
-                            DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON);
-
-                    if (mPendingRadioPowerOffAfterDataOff) {
-                        sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF,
-                                POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT);
-                    } else {
-                        log("powerOffRadioSafely: No data is connected, turn off radio now.");
-                        hangupAndPowerOff();
-                    }
-                    return;
+                // hang up all active voice calls first
+                if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
+                    mPhone.mCT.mRingingCall.hangupIfAlive();
+                    mPhone.mCT.mBackgroundCall.hangupIfAlive();
+                    mPhone.mCT.mForegroundCall.hangupIfAlive();
                 }
-                int dds = SubscriptionManager.getDefaultDataSubscriptionId();
-                // To minimize race conditions we call cleanUpAllConnections on
-                // both if else paths instead of before this isDisconnected test.
-                if (mPhone.areAllDataDisconnected()
-                        && (dds == mPhone.getSubId()
-                        || (dds != mPhone.getSubId()
-                        && ProxyController.getInstance().areAllDataDisconnected(dds)))) {
-                    // To minimize race conditions we do this after isDisconnected
-                    for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                        if (mPhone.getDcTracker(transport) != null) {
-                            mPhone.getDcTracker(transport).cleanUpAllConnections(
-                                    Phone.REASON_RADIO_TURNED_OFF);
-                        }
-                    }
-                    if (DBG) {
-                        log("powerOffRadioSafely: Data disconnected, turn off radio now.");
-                    }
-                    hangupAndPowerOff();
-                } else {
-                    // hang up all active voice calls first
-                    if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
-                        mPhone.mCT.mRingingCall.hangupIfAlive();
-                        mPhone.mCT.mBackgroundCall.hangupIfAlive();
-                        mPhone.mCT.mForegroundCall.hangupIfAlive();
-                    }
-                    for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                        if (mPhone.getDcTracker(transport) != null) {
-                            mPhone.getDcTracker(transport).cleanUpAllConnections(
-                                    Phone.REASON_RADIO_TURNED_OFF);
-                        }
-                    }
 
-                    if (dds != mPhone.getSubId()
-                            && !ProxyController.getInstance().areAllDataDisconnected(dds)) {
-                        if (DBG) {
-                            log(String.format("powerOffRadioSafely: Data is active on DDS (%d)."
-                                    + " Wait for all data disconnect", dds));
-                        }
-                        // Data is not disconnected on DDS. Wait for the data disconnect complete
-                        // before sending the RADIO_POWER off.
-                        ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
-                                EVENT_ALL_DATA_DISCONNECTED);
+                for (Phone phone : PhoneFactory.getPhones()) {
+                    if (!phone.getDataNetworkController().areAllDataDisconnected()) {
+                        log("powerOffRadioSafely: Data is active on phone " + phone.getSubId()
+                                + ". Wait for all data disconnect.");
                         mPendingRadioPowerOffAfterDataOff = true;
+                        phone.getDataNetworkController().registerDataNetworkControllerCallback(
+                                mDataDisconnectedCallback);
                     }
-                    Message msg = Message.obtain(this);
-                    msg.what = EVENT_SET_RADIO_POWER_OFF;
-                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
-                    if (sendMessageDelayed(msg, 30000)) {
-                        if (DBG) {
-                            log("powerOffRadioSafely: Wait up to 30s for data to disconnect, "
-                                    + "then turn off radio.");
-                        }
-                        mPendingRadioPowerOffAfterDataOff = true;
-                    } else {
-                        log("powerOffRadioSafely: Cannot send delayed Msg, turn off radio right"
-                                + " away.");
-                        hangupAndPowerOff();
-                        mPendingRadioPowerOffAfterDataOff = false;
-                    }
+                }
+
+                // Tear down outside of the disconnected check to prevent race conditions.
+                mPhone.getDataNetworkController().tearDownAllDataNetworks(
+                        DataNetwork.TEAR_DOWN_REASON_AIRPLANE_MODE_ON);
+
+                if (mPendingRadioPowerOffAfterDataOff) {
+                    sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF,
+                            POWER_OFF_ALL_DATA_NETWORKS_DISCONNECTED_TIMEOUT);
+                } else {
+                    log("powerOffRadioSafely: No data is connected, turn off radio now.");
+                    hangupAndPowerOff();
                 }
             }
         }
@@ -5130,7 +5003,6 @@
             if (mPendingRadioPowerOffAfterDataOff) {
                 if (DBG) log("Process pending request to turn radio off.");
                 hangupAndPowerOff();
-                mPendingRadioPowerOffAfterDataOffTag += 1;
                 mPendingRadioPowerOffAfterDataOff = false;
                 return true;
             }
@@ -5363,7 +5235,6 @@
         pw.println(" mDesiredPowerState=" + mDesiredPowerState);
         pw.println(" mRestrictedState=" + mRestrictedState);
         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
-        pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
         pw.println(" mCellIdentity=" + Rlog.pii(VDBG, mCellIdentity));
         pw.println(" mLastCellInfoReqTime=" + mLastCellInfoReqTime);
         dumpCellInfoList(pw);
@@ -5412,7 +5283,7 @@
         pw.println(" mImsRegistrationOnOff=" + mImsRegistrationOnOff);
         pw.println(" pending radio off event="
                 + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT));
-        pw.println(" mRadioDisabledByCarrier" + mRadioDisabledByCarrier);
+        pw.println(" mRadioPowerOffReasons=" + mRadioPowerOffReasons);
         pw.println(" mDeviceShuttingDown=" + mDeviceShuttingDown);
         pw.println(" mSpnUpdatePending=" + mSpnUpdatePending);
         pw.println(" mCellInfoMinIntervalMs=" + mCellInfoMinIntervalMs);
@@ -5969,25 +5840,6 @@
     }
 
     /**
-     * Registers for cell bandwidth changed.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     */
-    public void registerForBandwidthChanged(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        mBandwidthChangedRegistrants.add(r);
-    }
-
-    /**
-     * Unregisters for cell bandwidth changed.
-     * @param h handler to notify
-     */
-    public void unregisterForBandwidthChanged(Handler h) {
-        mBandwidthChangedRegistrants.remove(h);
-    }
-
-    /**
      * Get the NR data connection context ids.
      *
      * @return data connection context ids.
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index d950a9d..0c21ebf 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -423,7 +425,7 @@
                             (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0));
         }
 
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+        if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             int[] lteRsrqThresholds = mCarrierConfig.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
             if (lteRsrqThresholds != null) {
@@ -480,6 +482,18 @@
                                 AccessNetworkConstants.AccessNetworkType.NGRAN,
                                 (nrMeasurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0));
             }
+
+            int[] wcdmaEcnoThresholds = mCarrierConfig.getIntArray(
+                    CarrierConfigManager.KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY);
+            if (wcdmaEcnoThresholds != null) {
+                signalThresholdInfos.add(
+                        createSignalThresholdsInfo(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+                                wcdmaEcnoThresholds,
+                                AccessNetworkConstants.AccessNetworkType.UTRAN,
+                                false));
+            }
+
         }
 
         consolidatedAndSetReportingCriteria(signalThresholdInfos);
@@ -513,7 +527,7 @@
                         AccessNetworkConstants.AccessNetworkType.CDMA2000,
                         true));
 
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+        if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             signalThresholdInfos.add(
                     createSignalThresholdsInfo(
                             SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
@@ -546,6 +560,12 @@
                             AccessNetworkThresholds.NGRAN_SSSINR,
                             AccessNetworkConstants.AccessNetworkType.NGRAN,
                             false));
+            signalThresholdInfos.add(
+                    createSignalThresholdsInfo(
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+                            AccessNetworkThresholds.UTRAN_ECNO,
+                            AccessNetworkConstants.AccessNetworkType.UTRAN,
+                            false));
         }
 
         consolidatedAndSetReportingCriteria(signalThresholdInfos);
@@ -1108,6 +1128,16 @@
                 15, /* SIGNAL_STRENGTH_GOOD */
                 30  /* SIGNAL_STRENGTH_GREAT */
         };
+
+        /**
+         * List of dBm thresholds for UTRAN {@link AccessNetworkConstants.AccessNetworkType} ECNO
+         */
+        public static final int[] UTRAN_ECNO = new int[]{
+                -24, /* SIGNAL_STRENGTH_POOR */
+                -14, /* SIGNAL_STRENGTH_MODERATE */
+                -6, /* SIGNAL_STRENGTH_GOOD */
+                1  /* SIGNAL_STRENGTH_GREAT */
+        };
     }
 
     private static void log(String msg) {
diff --git a/src/java/com/android/internal/telephony/SimIndication.java b/src/java/com/android/internal/telephony/SimIndication.java
index 20f89da..d74a9b1 100644
--- a/src/java/com/android/internal/telephony/SimIndication.java
+++ b/src/java/com/android/internal/telephony/SimIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED;
@@ -53,7 +55,7 @@
      * @param indicationType Type of radio indication
      */
     public void carrierInfoForImsiEncryption(int indicationType) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
@@ -69,7 +71,7 @@
      * @param cdmaSource New CdmaSubscriptionSource
      */
     public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         int[] response = new int[]{cdmaSource};
         if (mRil.isLogOrTrace()) {
@@ -85,7 +87,7 @@
      * @param indicationType Type of radio indication
      */
     public void simPhonebookChanged(int indicationType) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
@@ -102,7 +104,7 @@
      */
     public void simPhonebookRecordsReceived(int indicationType, byte status,
             android.hardware.radio.sim.PhonebookRecordInfo[] records) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<>();
 
@@ -127,7 +129,7 @@
      */
     public void simRefresh(int indicationType,
             android.hardware.radio.sim.SimRefreshResult refreshResult) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         IccRefreshResponse response = new IccRefreshResponse();
         response.refreshResult = refreshResult.type;
@@ -144,7 +146,7 @@
      * @param indicationType Type of radio indication
      */
     public void simStatusChanged(int indicationType) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
 
@@ -159,7 +161,7 @@
      *        Refer to TS 102.223 section 9.4 for command types
      */
     public void stkEventNotify(int indicationType, String cmd) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
 
@@ -175,7 +177,7 @@
      *        Refer to TS 102.223 section 9.4 for command types
      */
     public void stkProactiveCommand(int indicationType, String cmd) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
 
@@ -189,7 +191,7 @@
      * @param indicationType Type of radio indication
      */
     public void stkSessionEnd(int indicationType) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
 
@@ -204,7 +206,7 @@
      * @param activate false for subscription deactivated, true for subscription activated
      */
     public void subscriptionStatusChanged(int indicationType, boolean activate) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         int[] response = new int[]{activate ? 1 : 0};
 
@@ -222,7 +224,7 @@
      * @param enabled Whether uiccApplications are enabled or disabled
      */
     public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
-        mRil.processIndication(RIL.SIM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_SIM, indicationType);
 
         if (mRil.isLogOrTrace()) {
             mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled);
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index b0099fb..5f05c58 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.sim.IRadioSimResponse;
@@ -41,7 +43,7 @@
 
     private void responseIccIo(RadioResponseInfo responseInfo,
             android.hardware.radio.sim.IccIoResult result) {
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
 
         if (rr != null) {
             IccIoResult ret = new IccIoResult(result.sw1, result.sw2, result.simResponse);
@@ -68,7 +70,7 @@
      */
     public void areUiccApplicationsEnabledResponse(RadioResponseInfo responseInfo,
             boolean enabled) {
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -83,7 +85,7 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void changeIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
@@ -91,14 +93,14 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void changeIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error.
      */
     public void enableUiccApplicationsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -109,7 +111,7 @@
     public void getAllowedCarriersResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.sim.CarrierRestrictions carrierRestrictions,
             int multiSimPolicy) {
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
         if (rr == null) {
             return;
         }
@@ -154,7 +156,7 @@
     public void getCdmaSubscriptionResponse(RadioResponseInfo responseInfo, String mdn,
             String hSid, String hNid, String min, String prl) {
         RadioResponse.responseStrings(
-                RIL.SIM_SERVICE, mRil, responseInfo, mdn, hSid, hNid, min, prl);
+                HAL_SERVICE_SIM, mRil, responseInfo, mdn, hSid, hNid, min, prl);
     }
 
     /**
@@ -162,7 +164,7 @@
      * @param source CDMA subscription source
      */
     public void getCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo, int source) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, source);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, source);
     }
 
     /**
@@ -171,7 +173,7 @@
      *        specified barring facility is active. "0" means "disabled for all"
      */
     public void getFacilityLockForAppResponse(RadioResponseInfo responseInfo, int response) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, response);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, response);
     }
 
     /**
@@ -180,7 +182,7 @@
      */
     public void getIccCardStatusResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.sim.CardStatus cardStatus) {
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
 
         if (rr != null) {
             IccCardStatus iccCardStatus = RILUtils.convertHalCardStatus(cardStatus);
@@ -197,7 +199,7 @@
      * @param imsi String containing the IMSI
      */
     public void getImsiForAppResponse(RadioResponseInfo responseInfo, String imsi) {
-        RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, imsi);
+        RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, imsi);
     }
 
     /**
@@ -207,7 +209,7 @@
     public void getSimPhonebookCapacityResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.sim.PhonebookCapacity pbCapacity) {
         AdnCapacity capacity = RILUtils.convertHalPhonebookCapacity(pbCapacity);
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
                 RadioResponse.sendMessageResponse(rr.mResult, capacity);
@@ -220,14 +222,14 @@
      * @param responseInfo Response info struct containing response type, serial no. and error.
      */
     public void getSimPhonebookRecordsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void iccCloseLogicalChannelResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -252,7 +254,7 @@
         for (int i = 0; i < selectResponse.length; i++) {
             arr.add((int) selectResponse[i]);
         }
-        RadioResponse.responseIntArrayList(RIL.SIM_SERVICE, mRil, responseInfo, arr);
+        RadioResponse.responseIntArrayList(HAL_SERVICE_SIM, mRil, responseInfo, arr);
     }
 
     /**
@@ -277,7 +279,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void reportStkServiceIsRunningResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -286,7 +288,7 @@
      */
     public void requestIccSimAuthenticationResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.sim.IccIoResult iccIo) {
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
 
         if (rr != null) {
             IccIoResult ret = new IccIoResult(iccIo.sw1, iccIo.sw2,
@@ -315,7 +317,7 @@
      *        string starting with first byte of response
      */
     public void sendEnvelopeResponse(RadioResponseInfo responseInfo, String commandResponse) {
-        RadioResponse.responseString(RIL.SIM_SERVICE, mRil, responseInfo, commandResponse);
+        RadioResponse.responseString(HAL_SERVICE_SIM, mRil, responseInfo, commandResponse);
     }
 
     /**
@@ -331,7 +333,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendTerminalResponseToSimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -339,7 +341,7 @@
      */
     public void setAllowedCarriersResponse(RadioResponseInfo responseInfo) {
         int ret = TelephonyManager.SET_CARRIER_RESTRICTION_ERROR;
-        RILRequest rr = mRil.processResponse(RIL.SIM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
         if (rr != null) {
             mRil.riljLog("setAllowedCarriersResponse - error = " + responseInfo.error);
 
@@ -355,14 +357,14 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCarrierInfoForImsiEncryptionResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -370,21 +372,21 @@
      * @param retry 0 is the number of retries remaining, or -1 if unknown
      */
     public void setFacilityLockForAppResponse(RadioResponseInfo responseInfo, int retry) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, retry);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, retry);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSimCardPowerResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setUiccSubscriptionResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.SIM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_SIM, mRil, responseInfo);
     }
 
     /**
@@ -392,7 +394,7 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void supplyIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
@@ -400,7 +402,7 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
@@ -408,7 +410,7 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void supplyIccPuk2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
@@ -416,7 +418,7 @@
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void supplyIccPukForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, remainingAttempts);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, remainingAttempts);
     }
 
     /**
@@ -428,7 +430,7 @@
     public void supplySimDepersonalizationResponse(RadioResponseInfo responseInfo, int persoType,
             int remainingRetries) {
         RadioResponse.responseInts(
-                RIL.SIM_SERVICE, mRil, responseInfo, persoType, remainingRetries);
+                HAL_SERVICE_SIM, mRil, responseInfo, persoType, remainingRetries);
     }
 
     /**
@@ -437,7 +439,7 @@
      */
     public void updateSimPhonebookRecordsResponse(RadioResponseInfo responseInfo,
             int updatedRecordIndex) {
-        RadioResponse.responseInts(RIL.SIM_SERVICE, mRil, responseInfo, updatedRecordIndex);
+        RadioResponse.responseInts(HAL_SERVICE_SIM, mRil, responseInfo, updatedRecordIndex);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 9f79161..1e947e5 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -26,6 +26,7 @@
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.BaseBundle;
 import android.os.Binder;
@@ -40,6 +41,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
 
@@ -47,6 +49,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Implements the ISmsImplBase interface used in the SmsManager API.
@@ -56,7 +59,8 @@
 
     private final Context mContext;
 
-    protected SmsController(Context context) {
+    @VisibleForTesting
+    public SmsController(Context context) {
         mContext = context;
         ServiceRegisterer smsServiceRegisterer = TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
@@ -151,6 +155,14 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        Rlog.d(LOG_TAG, "sendDataForSubscriber caller=" + callingPackage);
+
+        // Perform FDN check
+        if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+            return;
+        }
+
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort,
@@ -186,14 +198,60 @@
             String callingAttributionTag, String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent,
             boolean persistMessageForNonDefaultSmsApp, long messageId) {
+        sendTextForSubscriber(subId, callingPackage, callingAttributionTag, destAddr, scAddr,
+                text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
+                false, false);
+
+    }
+
+    /**
+     * @param subId Subscription Id
+     * @param callingAttributionTag the attribution tag of the caller
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success, or relevant errors
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     * @param skipFdnCheck if set to true, FDN check must be skipped .This is set in case of STK sms
+     *
+     * @hide
+     */
+    public void sendTextForSubscriber(int subId, String callingPackage,
+            String callingAttributionTag, String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipFdnCheck,
+            boolean skipShortCodeCheck) {
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage);
+
+        if (skipFdnCheck || skipShortCodeCheck) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_PHONE_STATE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires MODIFY_PHONE_STATE permission.");
+            }
+        }
         if (!getSmsPermissions(subId).checkCallingCanSendText(persistMessageForNonDefaultSmsApp,
                 callingPackage, callingAttributionTag, "Sending SMS message")) {
             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
             return;
         }
+
+        // Perform FDN check
+        if (!skipFdnCheck && isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+            return;
+        }
+
         long token = Binder.clearCallingIdentity();
         SubscriptionInfo info;
         try {
@@ -201,11 +259,12 @@
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+
         if (isBluetoothSubscription(info)) {
             sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
         } else {
             sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
-                    persistMessageForNonDefaultSmsApp, messageId);
+                    persistMessageForNonDefaultSmsApp, messageId, skipShortCodeCheck);
         }
     }
 
@@ -222,16 +281,17 @@
 
     private void sendIccText(int subId, String callingPackage, String destAddr,
             String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean persistMessageForNonDefaultSmsApp, long messageId) {
+            boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
         Rlog.d(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr"
-                + " Subscription: " + subId + " id: " + messageId);
+                + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
-                    deliveryIntent, persistMessageForNonDefaultSmsApp, messageId);
+                    deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
+                    skipShortCodeCheck);
         } else {
             Rlog.e(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr is null for"
-                    + " Subscription: " + subId + " id: " + messageId);
+                    + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         }
     }
@@ -259,6 +319,14 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        Rlog.d(LOG_TAG, "sendTextForSubscriberWithOptions caller=" + callingPackage);
+
+        // Perform FDN check
+        if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+            return;
+        }
+
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr,
@@ -281,6 +349,14 @@
         if (getCallingPackage() != null) {
             callingPackage = getCallingPackage();
         }
+        Rlog.d(LOG_TAG, "sendMultipartTextForSubscriber caller=" + callingPackage);
+
+        // Perform FDN check
+        if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+            return;
+        }
+
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr,
@@ -288,7 +364,7 @@
                     messageId);
         } else {
             Rlog.e(LOG_TAG, "sendMultipartTextForSubscriber iccSmsIntMgr is null for"
-                    + " Subscription: " + subId + " id: " + messageId);
+                    + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
             sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         }
     }
@@ -301,6 +377,14 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        Rlog.d(LOG_TAG, "sendMultipartTextForSubscriberWithOptions caller=" + callingPackage);
+
+        // Perform FDN check
+        if (isNumberBlockedByFDN(subId, destAddr, callingPackage)) {
+            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
+            return;
+        }
+
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag,
@@ -409,7 +493,7 @@
         // Don't show the SMS SIM Pick activity if it is not foreground.
         boolean isCallingProcessForeground = am != null
                 && am.getUidImportance(Binder.getCallingUid())
-                        == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+                == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
         if (!isCallingProcessForeground) {
             Rlog.d(LOG_TAG, "isSmsSimPickActivityNeeded: calling process not foreground. "
                     + "Suppressing activity.");
@@ -519,6 +603,8 @@
             throw new SecurityException("sendStoredText: Package " + callingPkg
                     + "does not belong to " + Binder.getCallingUid());
         }
+        Rlog.d(LOG_TAG, "sendStoredText caller=" + callingPkg);
+
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendStoredText(callingPkg, callingAttributionTag, messageUri, scAddress,
                     sentIntent, deliveryIntent);
@@ -537,6 +623,8 @@
             throw new SecurityException("sendStoredMultipartText: Package " + callingPkg
                     + " does not belong to " + Binder.getCallingUid());
         }
+        Rlog.d(LOG_TAG, "sendStoredMultipartText caller=" + callingPkg);
+
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingAttributionTag, messageUri,
                     scAddress, sentIntents, deliveryIntents);
@@ -714,6 +802,15 @@
     public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
             String callingAttributionTag, int subId, String number, int port, String text,
             PendingIntent sentIntent) {
+        Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber caller=" + callingPackage);
+
+        // Do not send non-emergency SMS in ECBM as it forces device to exit ECBM.
+        if(getPhone(subId).isInEcm()) {
+            Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber: Do not send non-emergency "
+                    + "SMS in ECBM as it forces device to exit ECBM.");
+            return;
+        }
+
         if (port == 0) {
             sendTextForSubscriberWithSelfPermissionsInternal(subId, callingPackage,
                     callingAttributionTag, number, null, text, sentIntent, null, false,
@@ -856,4 +953,49 @@
     public static String formatCrossStackMessageId(long id) {
         return "{x-message-id:" + id + "}";
     }
+
+    /**
+     * The following function checks if destination address or smsc is blocked due to FDN.
+     * @param subId subscription ID
+     * @param destAddr destination address of the message
+     * @return true if either destAddr or smscAddr is blocked due to FDN.
+     */
+    @VisibleForTesting
+    public boolean isNumberBlockedByFDN(int subId, String destAddr, String callingPackage) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        if (!FdnUtils.isFdnEnabled(phoneId)) {
+            return false;
+        }
+
+        // Skip FDN check for emergency numbers
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm.isEmergencyNumber(destAddr)) {
+            return false;
+        }
+
+        // Check if destAddr is present in FDN list
+        String defaultCountryIso = tm.getSimCountryIso().toUpperCase(Locale.ENGLISH);
+        if (FdnUtils.isNumberBlockedByFDN(phoneId, destAddr, defaultCountryIso)) {
+            return true;
+        }
+
+        // Get SMSC address for this subscription
+        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        String smscAddr;
+        if (iccSmsIntMgr != null) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                smscAddr =  iccSmsIntMgr.getSmscAddressFromIccEf(callingPackage);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            Rlog.e(LOG_TAG, "getSmscAddressFromIccEfForSubscriber iccSmsIntMgr is null"
+                    + " for Subscription: " + subId);
+            return true;
+        }
+
+        // Check if smscAddr is present in FDN list
+        return FdnUtils.isNumberBlockedByFDN(phoneId, smscAddr, defaultCountryIso);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 53556ac..1763b5f 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -29,6 +29,7 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.UserManager;
@@ -242,6 +243,21 @@
         }
     }
 
+    private String getSmscAddressFromUSIM(String callingPkg) {
+        IccSmsInterfaceManager iccSmsIntMgr = mPhone.getIccSmsInterfaceManager();
+        if (iccSmsIntMgr != null) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                return iccSmsIntMgr.getSmscAddressFromIccEf(callingPkg);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            Rlog.d(TAG, "getSmscAddressFromIccEf iccSmsIntMgr is null");
+        }
+        return null;
+    }
+
     private void reevaluateTimerStatus() {
         long currentTime = System.currentTimeMillis();
 
@@ -503,7 +519,8 @@
                             scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
                 } else {
                     pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
-                            scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+                            scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null,
+                            0, 0, 0, -1, tracker.mMessageRef);
                 }
             } else if (map.containsKey("data")) {
                 byte[] data = (byte[]) map.get("data");
@@ -518,7 +535,7 @@
                 } else {
                     pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
                             scAddr, destAddr, destPort.intValue(), data,
-                            (tracker.mDeliveryIntent != null));
+                            (tracker.mDeliveryIntent != null), tracker.mMessageRef);
                 }
             }
 
@@ -670,6 +687,9 @@
      */
     protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+        if (scAddr == null) {
+            scAddr = getSmscAddressFromUSIM(callingPackage);
+        }
         if (mImsSmsDispatcher.isAvailable()) {
             mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
                     deliveryIntent, isForVvm);
@@ -783,19 +803,128 @@
             PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
             int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
             long messageId) {
+        sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
+                persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+    }
+
+    /**
+     * Send a text based SMS.
+     *
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:<br>
+     *  <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+     *  <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>SmsManager.RESULT_INVALID_STATE</code><br>
+     *  <code>SmsManager.RESULT_NO_MEMORY</code><br>
+     *  <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+     *  <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+     *  <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+     *  <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+     *  <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+     *  <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+     *  <code>SmsManager.RESULT_CANCELLED</code><br>
+     *  <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+     *  <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     * @param messageUri optional URI of the message if it is already stored in the system
+     * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *  non-default SMS app.
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     * @param skipShortCodeCheck Skip check for short code type destination address.
+     */
+    public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
+            int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
+            long messageId, boolean skipShortCodeCheck) {
+        if (scAddr == null) {
+            scAddr = getSmscAddressFromUSIM(callingPkg);
+        }
         if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
             mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                     messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
-                    validityPeriod, isForVvm, messageId);
+                    validityPeriod, isForVvm, messageId, skipShortCodeCheck);
         } else {
             if (isCdmaMo()) {
                 mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                         messageUri, callingPkg, persistMessage, priority, expectMore,
-                        validityPeriod, isForVvm, messageId);
+                        validityPeriod, isForVvm, messageId, skipShortCodeCheck);
             } else {
                 mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                         messageUri, callingPkg, persistMessage, priority, expectMore,
-                        validityPeriod, isForVvm, messageId);
+                        validityPeriod, isForVvm, messageId, skipShortCodeCheck);
             }
         }
     }
@@ -909,6 +1038,9 @@
             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
             boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
             long messageId) {
+        if (scAddr == null) {
+            scAddr = getSmscAddressFromUSIM(callingPkg);
+        }
         if (mImsSmsDispatcher.isAvailable()) {
             mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
                     deliveryIntents, messageUri, callingPkg, persistMessage, priority,
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 8bcdc07..8e4ac60 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -43,6 +43,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -73,6 +74,8 @@
 
     private static final String SHORT_CODE_PATH = "/data/misc/sms/codes";
 
+    private static final String SHORT_CODE_VERSION_PATH = "/data/misc/sms/metadata/version";
+
     /** Default checking period for SMS sent without user permission. */
     private static final int DEFAULT_SMS_CHECK_PERIOD = 60000;      // 1 minute
 
@@ -128,6 +131,8 @@
     /** Last modified time for pattern file */
     private long mPatternFileLastModified = 0;
 
+    private int mPatternFileVersion = -1;
+
     private RoleManager mRoleManager;
 
     /** Directory for per-app SMS permission XML file. */
@@ -415,9 +420,11 @@
                     if (mPatternFile.exists()) {
                         if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from file");
                         mCurrentPatternMatcher = getPatternMatcherFromFile(countryIso);
+                        mPatternFileVersion = getPatternFileVersionFromFile();
                     } else {
                         if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from resource");
                         mCurrentPatternMatcher = getPatternMatcherFromResource(countryIso);
+                        mPatternFileVersion = -1;
                     }
                     mCurrentCountry = countryIso;
                 }
@@ -655,6 +662,37 @@
         return false;
     }
 
+    private int getPatternFileVersionFromFile() {
+        File versionFile = new File(SHORT_CODE_VERSION_PATH);
+        if (versionFile.exists()) {
+            BufferedReader reader = null;
+            try {
+                reader = new BufferedReader(new FileReader(versionFile));
+                String version = reader.readLine();
+                if (version != null) {
+                    return Integer.parseInt(version);
+                }
+            } catch (IOException e) {
+                Rlog.e(TAG, "File reader exception reading short code "
+                        + "pattern file version", e);
+            } finally {
+                try {
+                    if (reader != null) {
+                        reader.close();
+                    }
+                } catch (IOException e) {
+                    Rlog.e(TAG, "File reader exception closing short code "
+                            + "pattern file version reader", e);
+                }
+            }
+        }
+        return -1;
+    }
+
+    public int getShortCodeXmlFileVersion() {
+        return mPatternFileVersion;
+    }
+
     private static void log(String msg) {
         Rlog.d(TAG, msg);
     }
diff --git a/src/java/com/android/internal/telephony/SrvccConnection.java b/src/java/com/android/internal/telephony/SrvccConnection.java
new file mode 100644
index 0000000..b831a59
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SrvccConnection.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING;
+
+import android.net.Uri;
+import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.text.TextUtils;
+
+import com.android.ims.internal.ConferenceParticipant;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+
+/**
+ * Connection information for SRVCC
+ */
+public class SrvccConnection {
+    private static final String TAG = "SrvccConnection";
+
+    public static final int CALL_TYPE_NORMAL = 0;
+    public static final int CALL_TYPE_EMERGENCY = 1;
+
+    public static final int SUBSTATE_NONE = 0;
+    /** Pre-alerting state. Applicable for MT calls only */
+    public static final int SUBSTATE_PREALERTING = 1;
+
+    public static final int TONE_NONE = 0;
+    public static final int TONE_LOCAL = 1;
+    public static final int TONE_NETWORK = 2;
+
+    /** Values are CALL_TYPE_ */
+    private int mType = CALL_TYPE_NORMAL;
+
+    /** Values are Call.State */
+    private Call.State mState;
+
+    /** Values are SUBSTATE_ */
+    private int mSubstate = SUBSTATE_NONE;
+
+    /** Values are TONE_ */
+    private int mRingbackToneType = TONE_NONE;
+
+    /** true if it is a multi-party call */
+    private boolean mIsMpty = false;
+
+    /** true if it is a mobile terminated call */
+    private boolean mIsMT;
+
+    /** Remote party nummber */
+    private String mNumber;
+
+    /** Values are PhoneConstants.PRESENTATION_ */
+    private int mNumPresentation;
+
+    /** Remote party name */
+    private String mName;
+
+    /** Values are PhoneConstants.PRESENTATION_ */
+    private int mNamePresentation;
+
+    public SrvccConnection(ImsCallProfile profile,
+            ImsPhoneConnection c, @PreciseCallStates int preciseCallState) {
+        mState = toCallState(preciseCallState);
+        if (mState == Call.State.ALERTING) {
+            mRingbackToneType = isLocalTone(profile) ? TONE_LOCAL : TONE_NETWORK;
+        }
+        if (preciseCallState == PRECISE_CALL_STATE_INCOMING_SETUP) {
+            mSubstate = SUBSTATE_PREALERTING;
+        }
+
+        if (c == null) {
+            initialize(profile);
+        } else {
+            initialize(c);
+        }
+    }
+
+    public SrvccConnection(ConferenceParticipant cp, @PreciseCallStates int preciseCallState) {
+        mState = toCallState(preciseCallState);
+        mIsMT = cp.getCallDirection() == android.telecom.Call.Details.DIRECTION_INCOMING;
+        mNumber = getParticipantAddress(cp.getHandle());
+        mNumPresentation = cp.getParticipantPresentation();
+        if (mNumPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+            mNumber = "";
+        }
+        mName = cp.getDisplayName();
+        if (!TextUtils.isEmpty(mName)) {
+            mNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
+        } else {
+            mNamePresentation = PhoneConstants.PRESENTATION_UNKNOWN;
+        }
+        mIsMpty = true;
+    }
+
+    private static String getParticipantAddress(Uri address) {
+        if (address == null) {
+            return null;
+        }
+
+        String number = address.getSchemeSpecificPart();
+        if (TextUtils.isEmpty(number)) {
+            return null;
+        }
+
+        String[] numberParts = number.split("[@;:]");
+        if (numberParts.length == 0) return null;
+
+        return numberParts[0];
+    }
+
+    // MT call in alerting or prealerting state
+    private void initialize(ImsCallProfile profile) {
+        mIsMT = true;
+        mNumber = profile.getCallExtra(ImsCallProfile.EXTRA_OI);
+        mName = profile.getCallExtra(ImsCallProfile.EXTRA_CNA);
+        mNumPresentation = ImsCallProfile.OIRToPresentation(
+                profile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
+        mNamePresentation = ImsCallProfile.OIRToPresentation(
+                profile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
+    }
+
+    private void initialize(ImsPhoneConnection c) {
+        if (c.isEmergencyCall()) {
+            mType = CALL_TYPE_EMERGENCY;
+        }
+        mIsMT = c.isIncoming();
+        mNumber = c.getAddress();
+        mNumPresentation = c.getNumberPresentation();
+        mName = c.getCnapName();
+        mNamePresentation = c.getCnapNamePresentation();
+    }
+
+    private boolean isLocalTone(ImsCallProfile profile) {
+        if (profile == null) return false;
+
+        ImsStreamMediaProfile mediaProfile = profile.getMediaProfile();
+        if (mediaProfile == null) return false;
+
+        boolean shouldPlayRingback =
+                (mediaProfile.getAudioDirection() == ImsStreamMediaProfile.DIRECTION_INACTIVE)
+                        ? true : false;
+        return shouldPlayRingback;
+    }
+
+    private static Call.State toCallState(int preciseCallState) {
+        switch (preciseCallState) {
+            case PRECISE_CALL_STATE_ACTIVE: return Call.State.ACTIVE;
+            case PRECISE_CALL_STATE_HOLDING: return Call.State.HOLDING;
+            case PRECISE_CALL_STATE_DIALING: return Call.State.DIALING;
+            case PRECISE_CALL_STATE_ALERTING: return Call.State.ALERTING;
+            case PRECISE_CALL_STATE_INCOMING: return Call.State.INCOMING;
+            case PRECISE_CALL_STATE_WAITING: return Call.State.WAITING;
+            case PRECISE_CALL_STATE_INCOMING_SETUP: return Call.State.INCOMING;
+            default:
+        }
+        return Call.State.DISCONNECTED;
+    }
+
+    /** Returns the type of the call */
+    public int getType() {
+        return mType;
+    }
+
+    /** Returns the state */
+    public Call.State getState() {
+        return mState;
+    }
+
+    /** Updates the state */
+    public void setState(Call.State state) {
+        mState = state;
+    }
+
+    /** Returns the sub state */
+    public int getSubState() {
+        return mSubstate;
+    }
+
+    /** Returns the ringback tone type */
+    public int getRingbackToneType() {
+        return mRingbackToneType;
+    }
+
+    /** true if it is a multi-party call */
+    public boolean isMultiParty() {
+        return mIsMpty;
+    }
+
+    /** true if it is a mobile terminated call */
+    public boolean isIncoming() {
+        return mIsMT;
+    }
+
+    /** Returns the remote party nummber */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /** Returns the number presentation */
+    public int getNumberPresentation() {
+        return mNumPresentation;
+    }
+
+    /** Returns the remote party name */
+    public String getName() {
+        return mName;
+    }
+
+    /** Returns the name presentation */
+    public int getNamePresentation() {
+        return mNamePresentation;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SsDomainController.java b/src/java/com/android/internal/telephony/SsDomainController.java
new file mode 100644
index 0000000..b0c7dfd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SsDomainController.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.IWLAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_ACR;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_ALL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BAIC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BAOC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BIC_ROAM;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BIL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BOIC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_IBS;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_OBS;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_ALL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFB;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFNRC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFNRY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFU;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR;
+
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * The cache of the carrier configuration
+ */
+public class SsDomainController {
+    private static final String LOG_TAG = "SsDomainController";
+
+    /**
+     * A Helper class to carry the information indicating Ut is available or not.
+     */
+    public static class SuppServiceRoutingInfo {
+        private final boolean mUseSsOverUt;
+        private final boolean mSupportsCsfb;
+
+        public SuppServiceRoutingInfo(boolean useSsOverUt,
+                boolean isUtEnabled, boolean supportsCsfb) {
+            if (useSsOverUt) {
+                mUseSsOverUt = isUtEnabled;
+                mSupportsCsfb = supportsCsfb;
+            } else {
+                mUseSsOverUt = false;
+                mSupportsCsfb = true;
+            }
+        }
+
+        /**
+         * Returns whether Ut is available.
+         */
+        public boolean useSsOverUt() {
+            return mUseSsOverUt;
+        }
+
+        /**
+         * Returns whether CSFB is allowed.
+         */
+        public boolean supportsCsfb() {
+            return mSupportsCsfb;
+        }
+    }
+
+    public static final String SS_CW = "CW";
+    public static final String SS_CLIP = "CLIP";
+    public static final String SS_CLIR = "CLIR";
+    public static final String SS_COLP = "COLP";
+    public static final String SS_COLR = "COLR";
+
+    // Barring list of incoming numbers
+    public static final String CB_FACILITY_BIL = "BIL";
+    // Barring of all anonymous incoming number
+    public static final String CB_FACILITY_ACR = "ACR";
+
+    /**
+     * Network callback used to determine whether Wi-Fi is connected or not.
+     */
+    private ConnectivityManager.NetworkCallback mNetworkCallback =
+            new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onAvailable(Network network) {
+                    logi("Network available: " + network);
+                    updateWifiForUt(true);
+                }
+
+                @Override
+                public void onLost(Network network) {
+                    logi("Network lost: " + network);
+                    updateWifiForUt(false);
+                }
+
+                @Override
+                public void onUnavailable() {
+                    logi("Network unavailable");
+                    updateWifiForUt(false);
+                }
+            };
+
+    private final GsmCdmaPhone mPhone;
+
+    private final HashSet<String> mCbOverUtSupported = new HashSet<>();
+    private final HashSet<Integer> mCfOverUtSupported = new HashSet<>();
+    private final HashSet<String> mSsOverUtSupported = new HashSet<>();
+    private boolean mUtSupported = false;
+    private boolean mCsfbSupported = true;
+
+    private boolean mUtRequiresImsRegistration = false;
+    private boolean mUtAvailableWhenPsDataOff = false;
+    private boolean mUtAvailableWhenRoaming = false;
+    private Set<Integer> mUtAvailableRats = new HashSet<>();
+    private boolean mWiFiAvailable = false;
+    private boolean mIsMonitoringConnectivity = false;
+    /** true if Ims service handles the terminal-based service by itself. */
+    private boolean mOemHandlesTerminalBasedService = false;
+    private boolean mSupportsTerminalBasedCallWaiting = false;
+    private boolean mSupportsTerminalBasedClir = false;
+
+    public SsDomainController(GsmCdmaPhone phone) {
+        mPhone = phone;
+    }
+
+    /**
+     * Cache the configurations
+     */
+    public void updateSsOverUtConfig(PersistableBundle b) {
+        if (b == null) {
+            b = CarrierConfigManager.getDefaultConfig();
+        }
+
+        boolean supportsCsfb = b.getBoolean(
+                CarrierConfigManager.ImsSs.KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL);
+        boolean requiresImsRegistration = b.getBoolean(
+                CarrierConfigManager.ImsSs.KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL);
+        boolean availableWhenPsDataOff = b.getBoolean(
+                CarrierConfigManager.ImsSs.KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL);
+        boolean availableWhenRoaming = b.getBoolean(
+                CarrierConfigManager.ImsSs.KEY_UT_SUPPORTED_WHEN_ROAMING_BOOL);
+
+        boolean supportsUt = b.getBoolean(
+                CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL);
+        int[] services = b.getIntArray(
+                CarrierConfigManager.ImsSs.KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY);
+
+        int[] utRats = b.getIntArray(
+                CarrierConfigManager.ImsSs.KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY);
+
+        int[] tbServices = b.getIntArray(
+                CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY);
+
+        updateSsOverUtConfig(supportsUt, supportsCsfb, requiresImsRegistration,
+                availableWhenPsDataOff, availableWhenRoaming, services, utRats, tbServices);
+    }
+
+    private void updateSsOverUtConfig(boolean supportsUt, boolean supportsCsfb,
+            boolean requiresImsRegistration, boolean availableWhenPsDataOff,
+            boolean availableWhenRoaming, int[] services, int[] utRats, int[] tbServices) {
+
+        mUtSupported = supportsUt;
+        mCsfbSupported = supportsCsfb;
+        mUtRequiresImsRegistration = requiresImsRegistration;
+        mUtAvailableWhenPsDataOff = availableWhenPsDataOff;
+        mUtAvailableWhenRoaming = availableWhenRoaming;
+
+        mSupportsTerminalBasedCallWaiting = false;
+        mSupportsTerminalBasedClir = false;
+        if (tbServices != null) {
+            for (int tbService : tbServices) {
+                if (tbService == SUPPLEMENTARY_SERVICE_CW) {
+                    mSupportsTerminalBasedCallWaiting = true;
+                }
+                if (tbService == SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR) {
+                    mSupportsTerminalBasedClir = true;
+                }
+            }
+        }
+        logi("updateSsOverUtConfig terminal-based cw=" + mSupportsTerminalBasedCallWaiting
+                + ", clir=" + mSupportsTerminalBasedClir);
+
+        mCbOverUtSupported.clear();
+        mCfOverUtSupported.clear();
+        mSsOverUtSupported.clear();
+        mUtAvailableRats.clear();
+
+        if (!mUtSupported) {
+            logd("updateSsOverUtConfig Ut is not supported");
+            unregisterForConnectivityChanges();
+            return;
+        }
+
+        if (services != null) {
+            for (int service : services) {
+                updateConfig(service);
+            }
+        }
+
+        if (utRats != null) {
+            mUtAvailableRats = Arrays.stream(utRats).boxed().collect(Collectors.toSet());
+        }
+
+        if (mUtAvailableRats.contains(IWLAN)) {
+            registerForConnectivityChanges();
+        } else {
+            unregisterForConnectivityChanges();
+        }
+
+        logi("updateSsOverUtConfig supportsUt=" + mUtSupported
+                + ", csfb=" + mCsfbSupported
+                + ", regRequire=" + mUtRequiresImsRegistration
+                + ", whenPsDataOff=" + mUtAvailableWhenPsDataOff
+                + ", whenRoaming=" + mUtAvailableWhenRoaming
+                + ", cbOverUtSupported=" + mCbOverUtSupported
+                + ", cfOverUtSupported=" + mCfOverUtSupported
+                + ", ssOverUtSupported=" + mSsOverUtSupported
+                + ", utAvailableRats=" + mUtAvailableRats
+                + ", including IWLAN=" + mUtAvailableRats.contains(IWLAN));
+    }
+
+    private void updateConfig(int service) {
+        switch(service) {
+            case SUPPLEMENTARY_SERVICE_CW: mSsOverUtSupported.add(SS_CW); return;
+
+            case SUPPLEMENTARY_SERVICE_CF_ALL: mCfOverUtSupported.add(CF_REASON_ALL); return;
+            case SUPPLEMENTARY_SERVICE_CF_CFU:
+                mCfOverUtSupported.add(CF_REASON_UNCONDITIONAL);
+                return;
+            case SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING:
+                mCfOverUtSupported.add(CF_REASON_ALL_CONDITIONAL);
+                return;
+            case SUPPLEMENTARY_SERVICE_CF_CFB: mCfOverUtSupported.add(CF_REASON_BUSY); return;
+            case SUPPLEMENTARY_SERVICE_CF_CFNRY: mCfOverUtSupported.add(CF_REASON_NO_REPLY); return;
+            case SUPPLEMENTARY_SERVICE_CF_CFNRC:
+                mCfOverUtSupported.add(CF_REASON_NOT_REACHABLE);
+                return;
+
+            case SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP: mSsOverUtSupported.add(SS_CLIP); return;
+            case SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP: mSsOverUtSupported.add(SS_COLP); return;
+            case SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR: mSsOverUtSupported.add(SS_CLIR); return;
+            case SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR: mSsOverUtSupported.add(SS_COLR); return;
+
+            case SUPPLEMENTARY_SERVICE_CB_BAOC: mCbOverUtSupported.add(CB_FACILITY_BAOC); return;
+            case SUPPLEMENTARY_SERVICE_CB_BOIC: mCbOverUtSupported.add(CB_FACILITY_BAOIC); return;
+            case SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC:
+                mCbOverUtSupported.add(CB_FACILITY_BAOICxH);
+                return;
+            case SUPPLEMENTARY_SERVICE_CB_BAIC: mCbOverUtSupported.add(CB_FACILITY_BAIC); return;
+            case SUPPLEMENTARY_SERVICE_CB_BIC_ROAM:
+                mCbOverUtSupported.add(CB_FACILITY_BAICr);
+                return;
+            case SUPPLEMENTARY_SERVICE_CB_ACR: mCbOverUtSupported.add(CB_FACILITY_ACR); return;
+            case SUPPLEMENTARY_SERVICE_CB_BIL: mCbOverUtSupported.add(CB_FACILITY_BIL); return;
+            case SUPPLEMENTARY_SERVICE_CB_ALL: mCbOverUtSupported.add(CB_FACILITY_BA_ALL); return;
+            case SUPPLEMENTARY_SERVICE_CB_OBS: mCbOverUtSupported.add(CB_FACILITY_BA_MO); return;
+            case SUPPLEMENTARY_SERVICE_CB_IBS: mCbOverUtSupported.add(CB_FACILITY_BA_MT); return;
+
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Determines whether Ut service is available or not.
+     *
+     * @return {@code true} if Ut service is available
+     */
+    @VisibleForTesting
+    public boolean isUtEnabled() {
+        Phone imsPhone = mPhone.getImsPhone();
+        if (imsPhone == null) {
+            logd("isUtEnabled: called for GsmCdma");
+            return false;
+        }
+
+        if (!mUtSupported) {
+            logd("isUtEnabled: not supported");
+            return false;
+        }
+
+        if (mUtRequiresImsRegistration
+                && imsPhone.getServiceState().getState() != ServiceState.STATE_IN_SERVICE) {
+            logd("isUtEnabled: not registered");
+            return false;
+        }
+
+        if (isUtAvailableOnAnyTransport()) {
+            return imsPhone.isUtEnabled();
+        }
+
+        return false;
+    }
+
+    private boolean isMobileDataEnabled() {
+        boolean enabled;
+        int state = Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+                Settings.Global.MOBILE_DATA, -1);
+        if (state == -1) {
+            logi("isMobileDataEnabled MOBILE_DATA not found");
+            enabled = "true".equalsIgnoreCase(
+                    SystemProperties.get("ro.com.android.mobiledata", "true"));
+        } else {
+            enabled = (state != 0);
+        }
+        logi("isMobileDataEnabled enabled=" + enabled);
+        return enabled;
+    }
+
+    private boolean isUtAvailableOnAnyTransport() {
+        if (mUtAvailableWhenPsDataOff || isMobileDataEnabled()) {
+            if (isUtAvailableOverCellular()) {
+                logi("isUtAvailableOnAnyTransport found cellular");
+                return true;
+            }
+        }
+
+        logi("isUtAvailableOnAnyTransport wifiConnected=" + mWiFiAvailable);
+        if (mWiFiAvailable) {
+            if (mUtAvailableRats.contains(IWLAN)) {
+                logi("isUtAvailableOnAnyTransport found wifi");
+                return true;
+            }
+            logi("isUtAvailableOnAnyTransport wifi not support Ut");
+        }
+
+        logi("isUtAvailableOnAnyTransport no transport");
+        return false;
+    }
+
+    private boolean isUtAvailableOverCellular() {
+        NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (nri != null && nri.isRegistered()) {
+            if (!mUtAvailableWhenRoaming && nri.isRoaming()) {
+                logi("isUtAvailableOverCellular not available in roaming");
+                return false;
+            }
+
+            int networkType = nri.getAccessNetworkTechnology();
+            switch (networkType) {
+                case TelephonyManager.NETWORK_TYPE_NR:
+                    if (mUtAvailableRats.contains(NGRAN)) return true;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_LTE:
+                    if (mUtAvailableRats.contains(EUTRAN)) return true;
+                    break;
+
+                case TelephonyManager.NETWORK_TYPE_UMTS:
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                case TelephonyManager.NETWORK_TYPE_HSPAP:
+                    if (mUtAvailableRats.contains(UTRAN)) return true;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_GPRS:
+                case TelephonyManager.NETWORK_TYPE_EDGE:
+                case TelephonyManager.NETWORK_TYPE_GSM:
+                    if (mUtAvailableRats.contains(GERAN)) return true;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        logi("isUtAvailableOverCellular no cellular");
+        return false;
+    }
+
+    /**
+     * Updates the Wi-Fi connection state.
+     */
+    @VisibleForTesting
+    public void updateWifiForUt(boolean available) {
+        mWiFiAvailable = available;
+    }
+
+    /**
+     * Registers for changes to network connectivity.
+     */
+    private void registerForConnectivityChanges() {
+        if (mIsMonitoringConnectivity) {
+            return;
+        }
+
+        ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            logi("registerForConnectivityChanges");
+            NetworkRequest.Builder builder = new NetworkRequest.Builder();
+            builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+            cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+            mIsMonitoringConnectivity = true;
+        }
+    }
+
+    /**
+     * Unregisters for connectivity changes.
+     */
+    private void unregisterForConnectivityChanges() {
+        if (!mIsMonitoringConnectivity) {
+            return;
+        }
+
+        ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            logi("unregisterForConnectivityChanges");
+            cm.unregisterNetworkCallback(mNetworkCallback);
+            mIsMonitoringConnectivity = false;
+        }
+    }
+
+    /**
+     * Returns whether Ut is available for the given Call Barring service.
+     */
+    @VisibleForTesting
+    public boolean useCbOverUt(String facility) {
+        if (!mUtSupported) {
+            logd("useCbOverUt: Ut not supported");
+            return false;
+        }
+
+        return mCbOverUtSupported.contains(facility);
+    }
+
+    /**
+     * Returns whether Ut is available for the given Call Forwarding service.
+     */
+    @VisibleForTesting
+    public boolean useCfOverUt(int reason) {
+        if (!mUtSupported) {
+            logd("useCfOverUt: Ut not supported");
+            return false;
+        }
+
+        return mCfOverUtSupported.contains(reason);
+    }
+
+    /**
+     * Returns whether Ut is available for the given supplementary service.
+     */
+    @VisibleForTesting
+    public boolean useSsOverUt(String service) {
+        if (!mUtSupported) {
+            logd("useSsOverUt: Ut not supported");
+            return false;
+        }
+
+        return mSsOverUtSupported.contains(service);
+    }
+
+    /**
+     * Returns whether CSFB is supported for supplementary services.
+     */
+    public boolean supportCsfb() {
+        if (!mUtSupported) {
+            logd("supportsCsfb: Ut not supported");
+            return true;
+        }
+
+        return mCsfbSupported;
+    }
+
+    /**
+     * Returns SuppServiceRoutingInfo instance for the given Call Barring service.
+     * Only for ImsPhoneMmiCode.
+     */
+    public SuppServiceRoutingInfo getSuppServiceRoutingInfoForCb(String facility) {
+        return new SuppServiceRoutingInfo(useCbOverUt(facility), isUtEnabled(), supportCsfb());
+    }
+
+    /**
+     * Returns SuppServiceRoutingInfo instance for the given Call Forwarding service.
+     * Only for ImsPhoneMmiCode.
+     */
+    public SuppServiceRoutingInfo getSuppServiceRoutingInfoForCf(int reason) {
+        return new SuppServiceRoutingInfo(useCfOverUt(reason), isUtEnabled(), supportCsfb());
+    }
+
+    /**
+     * Returns SuppServiceRoutingInfo instance for the given supplementary service.
+     * Only for ImsPhoneMmiCode.
+     */
+    public SuppServiceRoutingInfo getSuppServiceRoutingInfoForSs(String service) {
+        if ((SS_CW.equals(service) && getOemHandlesTerminalBasedCallWaiting())
+                || (SS_CLIR.equals(service) && getOemHandlesTerminalBasedClir())) {
+            // Ims service handles the terminal based service by itself.
+            // Use legacy implementation. Forward the request to Ims service if Ut is available.
+            Phone imsPhone = mPhone.getImsPhone();
+            boolean isUtEnabled = (imsPhone != null) && imsPhone.isUtEnabled();
+            return new SuppServiceRoutingInfo(true, isUtEnabled, true);
+        }
+        return new SuppServiceRoutingInfo(useSsOverUt(service), isUtEnabled(), supportCsfb());
+    }
+
+    /**
+     * Returns SuppServiceRoutingInfo instance for a service will be served by Ut interface.
+     * Only for ImsPhoneMmiCode.
+     */
+    public SuppServiceRoutingInfo getSsRoutingOverUt() {
+        return new SuppServiceRoutingInfo(true, isUtEnabled(), true);
+    }
+
+    /**
+     * Set the carrier configuration for test.
+     * Test purpose only.
+     */
+    @VisibleForTesting
+    public void updateCarrierConfigForTest(boolean supportsUt, boolean supportsCsfb,
+            boolean requiresImsRegistration, boolean availableWhenPsDataOff,
+            boolean availableWhenRoaming, int[] services, int[] utRats, int[] tbServices) {
+        logi("updateCarrierConfigForTest supportsUt=" + supportsUt
+                +  ", csfb=" + supportsCsfb
+                + ", reg=" + requiresImsRegistration
+                + ", whenPsDataOff=" + availableWhenPsDataOff
+                + ", whenRoaming=" + availableWhenRoaming
+                + ", services=" + Arrays.toString(services)
+                + ", rats=" + Arrays.toString(utRats)
+                + ", tbServices=" + Arrays.toString(tbServices));
+
+        updateSsOverUtConfig(supportsUt, supportsCsfb, requiresImsRegistration,
+                availableWhenPsDataOff, availableWhenRoaming, services, utRats, tbServices);
+    }
+
+    /**
+     * @param state true if Ims service handles the terminal-based service by itself.
+     *              Otherwise, false.
+     */
+    public void setOemHandlesTerminalBasedService(boolean state) {
+        logi("setOemHandlesTerminalBasedService " + state);
+        mOemHandlesTerminalBasedService = state;
+    }
+
+    /**
+     * Returns whether the carrier supports the terminal-based call waiting service
+     * and Ims service handles it by itself.
+     */
+    public boolean getOemHandlesTerminalBasedCallWaiting() {
+        logi("getOemHandlesTerminalBasedCallWaiting "
+                + mSupportsTerminalBasedCallWaiting + ", " + mOemHandlesTerminalBasedService);
+        return mSupportsTerminalBasedCallWaiting && mOemHandlesTerminalBasedService;
+    }
+
+    /**
+     * Returns whether the carrier supports the terminal-based CLIR
+     * and Ims service handles it by itself.
+     */
+    public boolean getOemHandlesTerminalBasedClir() {
+        logi("getOemHandlesTerminalBasedClir "
+                + mSupportsTerminalBasedClir + ", " + mOemHandlesTerminalBasedService);
+        return mSupportsTerminalBasedClir && mOemHandlesTerminalBasedService;
+    }
+
+    /**
+     * Dump this instance into a readable format for dumpsys usage.
+     */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.increaseIndent();
+        pw.println("SsDomainController:");
+        pw.println(" mUtSupported=" + mUtSupported);
+        pw.println(" mCsfbSupported=" + mCsfbSupported);
+        pw.println(" mCbOverUtSupported=" + mCbOverUtSupported);
+        pw.println(" mCfOverUtSupported=" + mCfOverUtSupported);
+        pw.println(" mSsOverUtSupported=" + mSsOverUtSupported);
+        pw.println(" mUtRequiresImsRegistration=" + mUtRequiresImsRegistration);
+        pw.println(" mUtAvailableWhenPsDataOff=" + mUtAvailableWhenPsDataOff);
+        pw.println(" mUtAvailableWhenRoaming=" + mUtAvailableWhenRoaming);
+        pw.println(" mUtAvailableRats=" + mUtAvailableRats);
+        pw.println(" mWiFiAvailable=" + mWiFiAvailable);
+        pw.println(" mOemHandlesTerminalBasedService=" + mOemHandlesTerminalBasedService);
+        pw.println(" mSupportsTerminalBasedCallWaiting=" + mSupportsTerminalBasedCallWaiting);
+        pw.println(" mSupportsTerminalBasedClir=" + mSupportsTerminalBasedClir);
+        pw.decreaseIndent();
+    }
+
+    private void logi(String msg) {
+        Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+
+    private void logd(String msg) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 7afd5d2..c992983 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -37,6 +37,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
@@ -74,9 +75,9 @@
 import android.util.Log;
 
 import com.android.ims.ImsManager;
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.data.DataEnabledOverride;
 import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccUtils;
@@ -166,7 +167,7 @@
     protected UiccController mUiccController;
 
     /**
-     * Apps targeting on Android T and beyond will get an empty list if there is no access to device
+     * Apps targeting on Android T and beyond will get exception if there is no access to device
      * identifiers nor has carrier privileges when calling
      * SubscriptionManager#getSubscriptionsInGroup.
      */
@@ -310,11 +311,12 @@
             SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
             SubscriptionManager.DATA_ROAMING,
             SubscriptionManager.DISPLAY_NAME,
-            SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES,
+            SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES,
             SubscriptionManager.UICC_APPLICATIONS_ENABLED,
             SubscriptionManager.IMS_RCS_UCE_ENABLED,
             SubscriptionManager.CROSS_SIM_CALLING_ENABLED,
-            SubscriptionManager.NR_ADVANCED_CALLING_ENABLED
+            SubscriptionManager.NR_ADVANCED_CALLING_ENABLED,
+            SubscriptionManager.USER_HANDLE
     ));
 
     public static SubscriptionController init(Context c) {
@@ -465,6 +467,11 @@
                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
     }
 
+    private void enforceManageSubscriptionUserAssociation(String message) {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION, message);
+    }
+
     /**
      * Returns whether the {@code callingPackage} has access to subscriber identifiers on the
      * specified {@code subId} using the provided {@code message} in any resulting
@@ -498,17 +505,6 @@
     }
 
     /**
-     * Broadcast when SubscriptionInfo has changed
-     * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener
-     */
-     private void broadcastSimInfoContentChanged() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
-        mContext.sendBroadcast(intent);
-        intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
-        mContext.sendBroadcast(intent);
-     }
-
-    /**
      * Notify the changed of subscription info.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -519,9 +515,6 @@
         if (DBG) logd("notifySubscriptionInfoChanged:");
         trm.notifySubscriptionInfoChanged();
 
-        // FIXME: Remove if listener technique accepted.
-        broadcastSimInfoContentChanged();
-
         MultiSimSettingController.getInstance().notifySubscriptionInfoChanged();
         TelephonyMetrics metrics = TelephonyMetrics.getInstance();
         List<SubscriptionInfo> subInfos;
@@ -537,33 +530,34 @@
 
     /**
      * New SubInfoRecord instance and fill in detail info
-     * @param cursor
+     * @param cursor The database cursor
      * @return the query result of desired SubInfoRecord
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private SubscriptionInfo getSubInfoRecord(Cursor cursor) {
+        SubscriptionInfo.Builder builder = new SubscriptionInfo.Builder();
         int id = cursor.getInt(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
-        String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.ICC_ID));
-        int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.SIM_SLOT_INDEX));
-        String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.DISPLAY_NAME));
-        String carrierName = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.CARRIER_NAME));
-        int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.NAME_SOURCE));
-        int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.HUE));
-        String number = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.NUMBER));
-        int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.DATA_ROAMING));
-        String mcc = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.MCC_STRING));
-        String mnc = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.MNC_STRING));
+        builder.setId(id)
+                .setIccId(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.ICC_ID)))
+                .setSimSlotIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.SIM_SLOT_INDEX)))
+                .setDisplayName(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.DISPLAY_NAME)))
+                .setCarrierName(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.CARRIER_NAME)))
+                .setDisplayNameSource(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.NAME_SOURCE)))
+                .setIconTint(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.HUE)))
+                .setDataRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.DATA_ROAMING)))
+                .setMcc(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.MCC_STRING)))
+                .setMnc(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.MNC_STRING)));
+
         String ehplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.EHPLMNS));
         String hplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
@@ -571,77 +565,63 @@
         String[] ehplmns = ehplmnsRaw == null ? null : ehplmnsRaw.split(",");
         String[] hplmns = hplmnsRaw == null ? null : hplmnsRaw.split(",");
 
-        // cardId is the private ICCID/EID string, also known as the card string
-        String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
+        builder.setEhplmns(ehplmns).setHplmns(hplmns);
+
+
+        // CARD_ID is the private ICCID/EID string, also known as the card string
+        String cardString = cursor.getString(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.CARD_ID));
-        String countryIso = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.ISO_COUNTRY_CODE));
+        builder.setCardString(cardString);
         // publicCardId is the publicly exposed int card ID
-        int publicCardId = mUiccController.convertToPublicCardId(cardId);
+        int publicCardId = mUiccController.convertToPublicCardId(cardString);
+        builder.setCardId(publicCardId);
+
+        builder.setCountryIso(cursor.getString(cursor.getColumnIndexOrThrow(
+                SubscriptionManager.ISO_COUNTRY_CODE)))
+                .setCarrierId(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.CARRIER_ID)));
+
         boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.IS_EMBEDDED)) == 1;
-        int carrierId = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.CARRIER_ID));
-        UiccAccessRule[] accessRules;
+        builder.setEmbedded(isEmbedded);
         if (isEmbedded) {
-            accessRules = UiccAccessRule.decodeRules(cursor.getBlob(
-                    cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES)));
-        } else {
-            accessRules = null;
+            builder.setNativeAccessRules(UiccAccessRule.decodeRules(cursor.getBlob(
+                            cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES))));
         }
-        UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRules(cursor.getBlob(
-            cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS)));
-        boolean isOpportunistic = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.IS_OPPORTUNISTIC)) == 1;
-        String groupUUID = cursor.getString(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.GROUP_UUID));
-        int profileClass = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.PROFILE_CLASS));
-        int portIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.PORT_INDEX));
-        int subType = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.SUBSCRIPTION_TYPE));
-        String groupOwner = getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER,
-                /*defaultVal*/ null);
-        boolean areUiccApplicationsEnabled = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.UICC_APPLICATIONS_ENABLED)) == 1;
 
-        int usageSetting = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.USAGE_SETTING));
-
-        if (VDBG) {
-            String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
-            String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId);
-            logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:"
-                    + simSlotIndex + " carrierid:" + carrierId + " displayName:" + displayName
-                    + " nameSource:" + nameSource + " iconTint:" + iconTint
-                    + " dataRoaming:" + dataRoaming + " mcc:" + mcc + " mnc:" + mnc
-                    + " countIso:" + countryIso + " isEmbedded:"
-                    + isEmbedded + " accessRules:" + Arrays.toString(accessRules)
-                    + " carrierConfigAccessRules: " + Arrays.toString(carrierConfigAccessRules)
-                    + " cardId:" + cardIdToPrint + " portIndex:" + portIndex
-                    + " publicCardId:" + publicCardId
-                    + " isOpportunistic:" + isOpportunistic + " groupUUID:" + groupUUID
-                    + " profileClass:" + profileClass + " subscriptionType: " + subType
-                    + " areUiccApplicationsEnabled: " + areUiccApplicationsEnabled
-                    + " usageSetting: " + usageSetting);
-        }
+        builder.setCarrierConfigAccessRules(UiccAccessRule.decodeRules(cursor.getBlob(
+                cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS))))
+                .setOpportunistic(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.IS_OPPORTUNISTIC)) == 1)
+                .setGroupUuid(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.GROUP_UUID)))
+                .setProfileClass(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.PROFILE_CLASS)))
+                .setPortIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.PORT_INDEX)))
+                .setType(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.SUBSCRIPTION_TYPE)))
+                .setGroupOwner(getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER,
+                        /*defaultVal*/ null))
+                .setUiccApplicationsEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.UICC_APPLICATIONS_ENABLED)) == 1)
+                .setUsageSetting(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SubscriptionManager.USAGE_SETTING)));
 
         // If line1number has been set to a different number, use it instead.
+        String number = cursor.getString(cursor.getColumnIndexOrThrow(
+                SubscriptionManager.NUMBER));
         String line1Number = mTelephonyManager.getLine1Number(id);
         if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
             number = line1Number;
         }
+        builder.setNumber(number);
+
         // FIXME(b/210771052): constructing a complete SubscriptionInfo requires a port index,
         // but the port index isn't available here. Should it actually be part of SubscriptionInfo?
-        SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
-                carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
-                mcc, mnc, countryIso, isEmbedded, accessRules, cardId, publicCardId,
-                isOpportunistic, groupUUID, /* isGroupDisabled= */ false , carrierId, profileClass,
-                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
-                portIndex, usageSetting);
-        info.setAssociatedPlmns(ehplmns, hplmns);
-        return info;
+
+        return builder.build();
     }
 
     private String getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal) {
@@ -840,13 +820,14 @@
                 for (SubscriptionInfo si : subList) {
                     if (iccId.equals(si.getIccId())) {
                         if (DBG)
-                            logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
+                            logd("[getActiveSubInfoUsingIccId]+ iccId="
+                                    + Rlog.pii(LOG_TAG, iccId) + " subInfo=" + si);
                         return si;
                     }
                 }
             }
             if (DBG) {
-                logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
+                logd("[getActiveSubInfoUsingIccId]+ iccId=" + Rlog.pii(LOG_TAG, iccId)
                         + " subList=" + subList + " subInfo=null");
             }
         } finally {
@@ -1074,7 +1055,6 @@
      * @param callingFeatureId The feature in the package
      * @return all SIM count in database, include what was inserted before
      */
-    @Override
     public int getAllSubInfoCount(String callingPackage, String callingFeatureId) {
         if (DBG) logd("[getAllSubInfoCount]+");
 
@@ -1554,9 +1534,9 @@
                     if (!TextUtils.isEmpty(simCarrierName)) {
                         nameToSet = simCarrierName;
                     } else {
-                        nameToSet = "CARD " + Integer.toString(slotIndex + 1);
+                        Resources r = Resources.getSystem();
+                        nameToSet = r.getString(R.string.default_card_name, (slotIndex + 1));
                     }
-
                     ContentValues value = new ContentValues();
                     value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
                     resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value,
@@ -1955,7 +1935,7 @@
 
         String spn;
 
-        switch (subInfo.getNameSource()) {
+        switch (subInfo.getDisplayNameSource()) {
             case SubscriptionManager.NAME_SOURCE_SIM_PNN:
                 String pnn = phone.getPlmn();
                 return !TextUtils.isEmpty(pnn);
@@ -2023,7 +2003,7 @@
             // if there is no sub in the db, return 0 since subId does not exist in db
             if (allSubInfo == null || allSubInfo.isEmpty()) return 0;
             for (SubscriptionInfo subInfo : allSubInfo) {
-                int subInfoNameSource = subInfo.getNameSource();
+                int subInfoNameSource = subInfo.getDisplayNameSource();
                 boolean isHigherPriority = (getNameSourcePriority(subInfoNameSource)
                         > getNameSourcePriority(nameSource));
                 boolean isEqualPriorityAndName = (getNameSourcePriority(subInfoNameSource)
@@ -2045,7 +2025,9 @@
                 if (TextUtils.isEmpty(nameToSet)) {
                     if (nameSource == SubscriptionManager.NAME_SOURCE_USER_INPUT
                             && SubscriptionManager.isValidSlotIndex(getSlotIndex(subId))) {
-                        nameToSet = "CARD " + (getSlotIndex(subId) + 1);
+                        Resources r = Resources.getSystem();
+                        nameToSet = r.getString(R.string.default_card_name,
+                                (getSlotIndex(subId) + 1));
                     } else {
                         nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
                     }
@@ -2339,10 +2321,11 @@
             case SubscriptionManager.IMS_RCS_UCE_ENABLED:
             case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
             case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED:
+            case SubscriptionManager.USER_HANDLE:
                 values.put(propKey, cursor.getInt(columnIndex));
                 break;
             case SubscriptionManager.DISPLAY_NAME:
-            case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
+            case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES:
                 values.put(propKey, cursor.getString(columnIndex));
                 break;
             default:
@@ -2870,15 +2853,9 @@
                         subId);
 
         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
-        PhoneAccountHandle currentHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
-        logd("[setDefaultVoiceSubId] current phoneAccountHandle=" + currentHandle);
 
-        if (!Objects.equals(currentHandle, newHandle)) {
-            telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle);
-            logd("[setDefaultVoiceSubId] change to phoneAccountHandle=" + newHandle);
-        } else {
-            logd("[setDefaultVoiceSubId] default phoneAccountHandle not changed.");
-        }
+        telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle);
+        logd("[setDefaultVoiceSubId] requesting change to phoneAccountHandle=" + newHandle);
 
         if (previousDefaultSub != getDefaultSubId()) {
             sendDefaultChangedBroadcast(getDefaultSubId());
@@ -3197,8 +3174,8 @@
 
     /**
      * Get the SIM state for the slot index.
-     * For Remote-SIMs, this method returns {@link #IccCardConstants.State.UNKNOWN}
-     * @return SIM state as the ordinal of {@See IccCardConstants.State}
+     * For Remote-SIMs, this method returns {@link IccCardConstants.State#UNKNOWN}
+     * @return SIM state as the ordinal of {@link IccCardConstants.State}
      */
     @Override
     public int getSimStateForSlotIndex(int slotIndex) {
@@ -3292,6 +3269,7 @@
             case SubscriptionManager.VOIMS_OPT_IN_STATUS:
             case SubscriptionManager.NR_ADVANCED_CALLING_ENABLED:
             case SubscriptionManager.USAGE_SETTING:
+            case SubscriptionManager.USER_HANDLE:
                 value.put(propKey, Integer.parseInt(propValue));
                 break;
             case SubscriptionManager.ALLOWED_NETWORK_TYPES:
@@ -3379,7 +3357,7 @@
                         case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
                         case SubscriptionManager.IS_OPPORTUNISTIC:
                         case SubscriptionManager.GROUP_UUID:
-                        case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
+                        case SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES:
                         case SubscriptionManager.ALLOWED_NETWORK_TYPES:
                         case SubscriptionManager.D2D_STATUS_SHARING:
                         case SubscriptionManager.VOIMS_OPT_IN_STATUS:
@@ -3388,6 +3366,7 @@
                         case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER:
                         case SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS:
                         case SubscriptionManager.USAGE_SETTING:
+                        case SubscriptionManager.USER_HANDLE:
                             resultValue = cursor.getString(0);
                             break;
                         default:
@@ -3432,12 +3411,7 @@
             pw.println(" defaultDataSubId=" + getDefaultDataSubId());
             pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
             pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
-
-            pw.println(" defaultDataPhoneId=" + SubscriptionManager
-                    .from(mContext).getDefaultDataPhoneId());
             pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
-            pw.println(" defaultSmsPhoneId=" + SubscriptionManager
-                    .from(mContext).getDefaultSmsPhoneId());
             pw.flush();
 
             for (Entry<Integer, ArrayList<Integer>> entry : mSlotIndexToSubIds.entrySet()) {
@@ -3636,7 +3610,7 @@
                 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
             }
 
-            return phoneSwitcher.getOpportunisticDataSubscriptionId();
+            return phoneSwitcher.getAutoSelectedDataSubId();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -4033,20 +4007,22 @@
         }
 
         // If the calling app neither has carrier privileges nor READ_PHONE_STATE and access to
-        // device identifiers, it will return an empty list.
+        // device identifiers, it will throw a securityException.
         if (CompatChanges.isChangeEnabled(
                 REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID, Binder.getCallingUid())) {
             try {
                 if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
                         callingPackage, callingFeatureId, "getSubscriptionsInGroup")) {
                     EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
-                    return new ArrayList<>();
+                    throw new SecurityException("Need to have carrier privileges or access to "
+                            + "device identifiers to call getSubscriptionsInGroup");
                 }
             } catch (SecurityException e) {
                 EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
-                return new ArrayList<>();
+                throw e;
             }
         }
+
         return subInfoList.stream().filter(info -> {
             if (!groupUuid.equals(info.getGroupUuid())) return false;
             int subId = info.getSubscriptionId();
@@ -4192,7 +4168,10 @@
         }
 
         // Can't find the existing SIM.
-        if (slotInfo == null) return false;
+        if (slotInfo == null) {
+            loge("Can't find the existing SIM.");
+            return false;
+        }
 
         // this for physical slot which has only one port
         if (enable && !slotInfo.getPorts().stream().findFirst().get().isActive()) {
@@ -4442,16 +4421,16 @@
         if (hasIdentifierAccess && hasPhoneNumberAccess) {
             return subInfo;
         }
-        SubscriptionInfo result = new SubscriptionInfo(subInfo);
+        SubscriptionInfo.Builder result = new SubscriptionInfo.Builder(subInfo);
         if (!hasIdentifierAccess) {
-            result.clearIccId();
-            result.clearCardString();
-            result.clearGroupUuid();
+            result.setIccId(null);
+            result.setCardString(null);
+            result.setGroupUuid(null);
         }
         if (!hasPhoneNumberAccess) {
-            result.clearNumber();
+            result.setNumber(null);
         }
-        return result;
+        return result.build();
     }
 
     private synchronized boolean addToSubIdList(int slotIndex, int subId, int subscriptionType) {
@@ -4526,20 +4505,23 @@
 
             mCacheOpportunisticSubInfoList = subList;
 
-            for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
+            for (int i = 0; i < mCacheOpportunisticSubInfoList.size(); i++) {
+                SubscriptionInfo info = mCacheOpportunisticSubInfoList.get(i);
                 if (shouldDisableSubGroup(info.getGroupUuid())) {
-                    info.setGroupDisabled(true);
+                    SubscriptionInfo.Builder builder = new SubscriptionInfo.Builder(info);
+                    builder.setGroupDisabled(true);
+                    mCacheOpportunisticSubInfoList.set(i, builder.build());
                 }
             }
 
             if (DBG_CACHE) {
                 if (!mCacheOpportunisticSubInfoList.isEmpty()) {
                     for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) {
-                        logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info="
-                                + si);
+                        logd("[refreshCachedOpportunisticSubscriptionInfoList] Setting Cached "
+                                + "info=" + si);
                     }
                 } else {
-                    logdl("[refreshCachedOpptSubscriptionInfoList]- no info return");
+                    logdl("[refreshCachedOpportunisticSubscriptionInfoList]- no info return");
                 }
             }
 
@@ -4564,19 +4546,19 @@
     }
 
     /**
-     * Set allowing mobile data during voice call.
+     * Set enabled mobile data policies.
      *
      * @param subId Subscription index
-     * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride}
-     * for details.
+     * @param policies Mobile data policies in string format.
+     *                 See {@link TelephonyManager.MobileDataPolicy} for details.
      * @return {@code true} if settings changed, otherwise {@code false}.
      */
-    public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) {
-        if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId);
+    public boolean setEnabledMobileDataPolicies(int subId, @NonNull String policies) {
+        if (DBG) logd("[setEnabledMobileDataPolicies]+ policies:" + policies + " subId:" + subId);
 
         validateSubId(subId);
         ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules);
+        value.put(SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES, policies);
 
         boolean result = updateDatabase(value, subId, true) > 0;
 
@@ -4590,15 +4572,16 @@
     }
 
     /**
-     * Get data enabled override rules.
+     * Get enabled mobile data policies.
      *
      * @param subId Subscription index
-     * @return Data enabled override rules in string
+     * @return Enabled mobile data policies joined by "," (ie. "1,2") or an empty string if no
+     * policies are enabled.
      */
     @NonNull
-    public String getDataEnabledOverrideRules(int subId) {
+    public String getEnabledMobileDataPolicies(int subId) {
         return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId,
-                SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
+                SubscriptionManager.ENABLED_MOBILE_DATA_POLICIES));
     }
 
     /**
@@ -4720,7 +4703,7 @@
      * Sets the phone number for the given {@code subId}.
      *
      * <p>The only accepted {@code source} is {@link
-     * SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER}.
+     * SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER}.
      */
     @Override
     public void setPhoneNumber(int subId, int source, String number,
@@ -4787,6 +4770,130 @@
     }
 
     /**
+     * Querying last used TP - MessageRef for particular subId from SIMInfo table.
+     * @return messageRef
+     */
+    public int getMessageRef(int subId) {
+        try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+                new String[]{SubscriptionManager.TP_MESSAGE_REF},
+                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=\"" + subId
+                        + "\"", null, null)) {
+            try {
+                if (cursor != null && cursor.moveToFirst()) {
+                    do {
+                        return cursor.getInt(cursor.getColumnIndexOrThrow(
+                                SubscriptionManager.TP_MESSAGE_REF));
+                    } while (cursor.moveToNext());
+                } else {
+                    if (DBG) logd("Valid row not present in db");
+                }
+            } catch (Exception e) {
+                if (DBG) logd("Query failed " + e.getMessage());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Update the TP - Message Reference value for every SMS Sent
+     * @param messageRef
+     * @param subId
+     */
+    public void updateMessageRef(int subId, int messageRef) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mContext, subId, mContext.getOpPackageName());
+
+        if (mContext == null) {
+            logel("[updateMessageRef] mContext is null");
+            return;
+        }
+        try {
+            if (SubscriptionManager.CONTENT_URI != null) {
+                ContentValues values = new ContentValues(1);
+                values.put(SubscriptionManager.TP_MESSAGE_REF, messageRef);
+                mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, values,
+                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=\"" + subId
+                                + "\"", null);
+            } else {
+                if (DBG) logd("TP - Message reference value not updated to DB");
+            }
+        } finally {
+            if (DBG) logd("TP - Message reference updated to DB Successfully :" + messageRef);
+        }
+    }
+
+    /**
+     * Set UserHandle for this subscription
+     *
+     * @param userHandle the userHandle associated with the subscription
+     * Pass {@code null} user handle to clear the association
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated.
+     *
+     * @throws SecurityException if doesn't have required permission.
+     * @throws IllegalArgumentException if subId is invalid.
+     */
+    @Override
+    public int setSubscriptionUserHandle(@Nullable UserHandle userHandle, int subId) {
+        enforceManageSubscriptionUserAssociation("setSubscriptionUserHandle");
+
+        if (userHandle == null) {
+            userHandle = UserHandle.of(UserHandle.USER_NULL);
+        }
+
+        long token = Binder.clearCallingIdentity();
+        try {
+            int ret = setSubscriptionProperty(subId, SubscriptionManager.USER_HANDLE,
+                    String.valueOf(userHandle.getIdentifier()));
+            // ret is the number of records updated in the DB
+            if (ret != 0) {
+                notifySubscriptionInfoChanged();
+            } else {
+                throw new IllegalArgumentException("[setSubscriptionUserHandle]: Invalid subId: "
+                        + subId);
+            }
+            return ret;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Get UserHandle of this subscription.
+     *
+     * @param subId the unique SubscriptionInfo index in database
+     * @return userHandle associated with this subscription
+     * or {@code null} if subscription is not associated with any user.
+     *
+     * @throws SecurityException if doesn't have required permission.
+     * @throws IllegalArgumentException if subId is invalid.
+     */
+    @Override
+    public UserHandle getSubscriptionUserHandle(int subId) {
+        enforceManageSubscriptionUserAssociation("getSubscriptionUserHandle");
+
+        long token = Binder.clearCallingIdentity();
+        try {
+            String userHandleStr = getSubscriptionProperty(subId, SubscriptionManager.USER_HANDLE);
+            if (userHandleStr == null) {
+                throw new IllegalArgumentException("[getSubscriptionUserHandle]: Invalid subId: "
+                        + subId);
+            }
+            UserHandle userHandle = UserHandle.of(Integer.parseInt(userHandleStr));
+            if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
+                return null;
+            }
+            return userHandle;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
      * @hide
      */
     private void setGlobalSetting(String name, int value) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 60f9a62..5965899 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -612,7 +612,8 @@
 
         /**
          * The sim loading sequence will be
-         *  1. ACTION_SUBINFO_CONTENT_CHANGE happens through updateSubscriptionInfoByIccId() above.
+         *  1. OnSubscriptionsChangedListener is called through updateSubscriptionInfoByIccId()
+         *  above.
          *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
          *  /ACTION_SIM_APPLICATION_STATE_CHANGED
          *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
@@ -821,7 +822,7 @@
         // If SIM is not absent, insert new record or update existing record.
         if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId]) && sIccId[phoneId] != null) {
             logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
-                    + sIccId[phoneId] + ", phoneId:" + phoneId);
+                    + Rlog.pii(LOG_TAG, sIccId[phoneId]) + ", phoneId:" + phoneId);
             mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId);
         }
 
@@ -1017,7 +1018,7 @@
                 mSubscriptionController.insertEmptySubInfoRecord(
                         embeddedProfile.getIccid(), SubscriptionManager.SIM_NOT_INSERTED);
             } else {
-                nameSource = existingSubscriptions.get(index).getNameSource();
+                nameSource = existingSubscriptions.get(index).getDisplayNameSource();
                 prevCarrierId = existingSubscriptions.get(index).getCarrierId();
                 existingSubscriptions.remove(index);
             }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index dcbd2d5..08c02e2 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -27,7 +27,6 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructStatVfs;
-import android.telephony.AccessNetworkConstants.TransportType;
 import android.text.TextUtils;
 
 import com.android.ims.ImsManager;
@@ -40,9 +39,6 @@
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
 import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -313,10 +309,6 @@
         return new SimActivationTracker(phone);
     }
 
-    public DcTracker makeDcTracker(Phone phone, @TransportType int transportType) {
-        return new DcTracker(phone, transportType);
-    }
-
     public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
         return new CarrierSignalAgent(phone);
     }
@@ -404,10 +396,6 @@
         return new DeviceStateMonitor(phone);
     }
 
-    public TransportManager makeTransportManager(Phone phone) {
-        return new TransportManager(phone);
-    }
-
     /**
      * Make access networks manager
      *
@@ -430,10 +418,6 @@
         return new LocaleTracker(phone, nitzStateMachine, looper);
     }
 
-    public DataEnabledSettings makeDataEnabledSettings(Phone phone) {
-        return new DataEnabledSettings(phone);
-    }
-
     public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             int phoneId, int precisePhoneType,
             TelephonyComponentFactory telephonyComponentFactory) {
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 40bb212..b9e04c8 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -62,7 +63,6 @@
  *
  */
 public class TelephonyTester {
-    private static final String LOG_TAG = "TelephonyTester";
     private static final boolean DBG = true;
 
     /**
@@ -144,6 +144,7 @@
             "com.android.internal.telephony.TestServiceState";
 
     private static final String EXTRA_ACTION = "action";
+    private static final String EXTRA_PHONE_ID = "phone_id";
     private static final String EXTRA_VOICE_RAT = "voice_rat";
     private static final String EXTRA_DATA_RAT = "data_rat";
     private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state";
@@ -157,6 +158,8 @@
 
     private static final String ACTION_RESET = "reset";
 
+    private String mLogTag;
+
     private static List<ImsExternalCallState> mImsExternalCallStates = null;
 
     private Intent mServiceStateTestIntent;
@@ -198,11 +201,7 @@
                     sendTestSuppServiceNotification(intent);
                 } else if (action.equals(ACTION_TEST_SERVICE_STATE)) {
                     log("handle test service state changed intent");
-                    // Trigger the service state update. The replacement will be done in
-                    // overrideServiceState().
-                    mServiceStateTestIntent = intent;
-                    mPhone.getServiceStateTracker().sendEmptyMessage(
-                            ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
+                    setServiceStateTestIntent(intent);
                 } else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
                     log("handle test IMS ecall intent");
                     testImsECall();
@@ -216,7 +215,7 @@
                     if (DBG) log("onReceive: unknown action=" + action);
                 }
             } catch (BadParcelableException e) {
-                Rlog.w(LOG_TAG, e);
+                Rlog.w(mLogTag, e);
             }
         }
     };
@@ -225,6 +224,7 @@
         mPhone = phone;
 
         if (TelephonyUtils.IS_DEBUGGABLE) {
+            mLogTag = "TelephonyTester-" + mPhone.getPhoneId();
             IntentFilter filter = new IntentFilter();
 
             filter.addAction(mPhone.getActionDetached());
@@ -261,8 +261,8 @@
         }
     }
 
-    private static void log(String s) {
-        Rlog.d(LOG_TAG, s);
+    private void log(String s) {
+        Rlog.d(mLogTag, s);
     }
 
     private void handleSuppServiceFailedIntent(Intent intent) {
@@ -385,8 +385,25 @@
         }
     }
 
+    /**
+     * Set the service state test intent.
+     *
+     * @param intent The service state test intent.
+     */
+    public void setServiceStateTestIntent(@NonNull Intent intent) {
+        mServiceStateTestIntent = intent;
+        // Trigger the service state update. The replacement will be done in
+        // overrideServiceState().
+        mPhone.getServiceStateTracker().sendEmptyMessage(
+                ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
+    }
+
     void overrideServiceState(ServiceState ss) {
         if (mServiceStateTestIntent == null || ss == null) return;
+        if (mPhone.getPhoneId() != mServiceStateTestIntent.getIntExtra(
+                EXTRA_PHONE_ID, mPhone.getPhoneId())) {
+            return;
+        }
         if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION)
                 && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) {
             log("Service state override reset");
@@ -394,13 +411,36 @@
         }
 
         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
+            int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
+                    ServiceState.STATE_OUT_OF_SERVICE);
             ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
                     ServiceState.STATE_OUT_OF_SERVICE));
+            NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
+            if (state == ServiceState.STATE_IN_SERVICE) {
+                builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+            } else {
+                builder.setRegistrationState(
+                        NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+            }
+            ss.addNetworkRegistrationInfo(builder.build());
             log("Override voice service state with " + ss.getState());
         }
         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
-            ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
-                    ServiceState.STATE_OUT_OF_SERVICE));
+            int state = mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
+                    ServiceState.STATE_OUT_OF_SERVICE);
+            ss.setDataRegState(state);
+            NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            NetworkRegistrationInfo.Builder builder = new NetworkRegistrationInfo.Builder(nri);
+            if (state == ServiceState.STATE_IN_SERVICE) {
+                builder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+            } else {
+                builder.setRegistrationState(
+                        NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+            }
+            ss.addNetworkRegistrationInfo(builder.build());
             log("Override data service state with " + ss.getDataRegistrationState());
         }
         if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
diff --git a/src/java/com/android/internal/telephony/VoiceIndication.java b/src/java/com/android/internal/telephony/VoiceIndication.java
index 984d2a0..9720bb7 100644
--- a/src/java/com/android/internal/telephony/VoiceIndication.java
+++ b/src/java/com/android/internal/telephony/VoiceIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_CALL_WAITING;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_INFO_REC;
@@ -63,7 +65,7 @@
      */
     public void callRing(int indicationType, boolean isGsm,
             android.hardware.radio.voice.CdmaSignalInfoRecord record) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         char[] response = null;
 
@@ -90,7 +92,7 @@
      * @param indicationType Type of radio indication
      */
     public void callStateChanged(int indicationType) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
 
@@ -104,7 +106,7 @@
      */
     public void cdmaCallWaiting(int indicationType,
             android.hardware.radio.voice.CdmaCallWaiting callWaitingRecord) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         // TODO: create a CdmaCallWaitingNotification constructor that takes in these fields to make
         // sure no fields are missing
@@ -134,7 +136,7 @@
      */
     public void cdmaInfoRec(int indicationType,
             android.hardware.radio.voice.CdmaInformationRecord[] records) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         for (int i = 0; i < records.length; i++) {
             android.hardware.radio.voice.CdmaInformationRecord record = records[i];
@@ -233,7 +235,7 @@
      * @param status CDMA OTA provision status
      */
     public void cdmaOtaProvisionStatus(int indicationType, int status) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         int[] response = new int[] {status};
 
@@ -252,7 +254,7 @@
      */
     public void currentEmergencyNumberList(int indicationType,
             android.hardware.radio.voice.EmergencyNumber[] emergencyNumberList) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         List<EmergencyNumber> response = new ArrayList<>(emergencyNumberList.length);
         for (android.hardware.radio.voice.EmergencyNumber enHal : emergencyNumberList) {
@@ -279,7 +281,7 @@
      * @param indicationType Type of radio indication
      */
     public void enterEmergencyCallbackMode(int indicationType) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
 
@@ -294,7 +296,7 @@
      * @param indicationType Type of radio indication
      */
     public void exitEmergencyCallbackMode(int indicationType) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
 
@@ -307,7 +309,7 @@
      * @param start true = start play ringback tone, false = stop playing ringback tone
      */
     public void indicateRingbackTone(int indicationType, boolean start) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
 
@@ -322,7 +324,7 @@
      */
     public void onSupplementaryServiceIndication(int indicationType,
             android.hardware.radio.voice.StkCcUnsolSsResult ss) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         int num;
         SsData ssData = new SsData();
@@ -374,7 +376,7 @@
      * @param msg Message string in UTF-8, if applicable
      */
     public void onUssd(int indicationType, int ussdModeType, String msg) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
 
@@ -390,7 +392,7 @@
      * @param indicationType Type of radio indication
      */
     public void resendIncallMute(int indicationType) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
 
@@ -403,7 +405,7 @@
      * @param state New SRVCC State
      */
     public void srvccStateNotify(int indicationType, int state) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         int[] response = new int[] {state};
 
@@ -419,7 +421,7 @@
      * @param alpha ALPHA string from UICC in UTF-8 format
      */
     public void stkCallControlAlphaNotify(int indicationType, String alpha) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
 
@@ -434,7 +436,7 @@
      * @param timeout Timeout value in milliseconds for setting up voice call
      */
     public void stkCallSetup(int indicationType, long timeout) {
-        mRil.processIndication(RIL.VOICE_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_VOICE, indicationType);
 
         if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
 
diff --git a/src/java/com/android/internal/telephony/VoiceResponse.java b/src/java/com/android/internal/telephony/VoiceResponse.java
index b1ba9d9..d1e060e 100644
--- a/src/java/com/android/internal/telephony/VoiceResponse.java
+++ b/src/java/com/android/internal/telephony/VoiceResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.voice.IRadioVoiceResponse;
@@ -47,49 +49,49 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acceptCallResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void cancelPendingUssdResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void conferenceResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void dialResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void emergencyDialResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void exitEmergencyCallbackModeResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void explicitCallTransferResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
@@ -99,7 +101,7 @@
      */
     public void getCallForwardStatusResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.voice.CallForwardInfo[] callForwardInfos) {
-        RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
         if (rr != null) {
             CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.length];
             for (int i = 0; i < callForwardInfos.length; i++) {
@@ -129,7 +131,7 @@
     public void getCallWaitingResponse(RadioResponseInfo responseInfo, boolean enable,
             int serviceClass) {
         RadioResponse.responseInts(
-                RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0, serviceClass);
+                HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0, serviceClass);
     }
 
     /**
@@ -137,7 +139,7 @@
      * @param status indicates CLIP status
      */
     public void getClipResponse(RadioResponseInfo responseInfo, int status) {
-        RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, status);
+        RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, status);
     }
 
     /**
@@ -146,7 +148,7 @@
      * @param m is "m" parameter from TS 27.007 7.7
      */
     public void getClirResponse(RadioResponseInfo responseInfo, int n, int m) {
-        RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, n, m);
+        RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, n, m);
     }
 
     /**
@@ -155,7 +157,7 @@
      */
     public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.voice.Call[] calls) {
-        RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
 
         if (rr != null) {
             int num = calls.length;
@@ -198,7 +200,7 @@
      */
     public void getLastCallFailCauseResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.voice.LastCallFailCauseInfo fcInfo) {
-        RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
 
         if (rr != null) {
             LastCallFailCause ret = new LastCallFailCause();
@@ -216,7 +218,7 @@
      * @param enable true for "mute enabled" and false for "mute disabled"
      */
     public void getMuteResponse(RadioResponseInfo responseInfo, boolean enable) {
-        RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0);
+        RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0);
     }
 
     /**
@@ -225,7 +227,7 @@
      *        true for Enhanced Privacy Mode (Private Long Code Mask)
      */
     public void getPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo, boolean enable) {
-        RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, enable ? 1 : 0);
+        RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, enable ? 1 : 0);
     }
 
     /**
@@ -233,35 +235,35 @@
      * @param mode TTY mode
      */
     public void getTtyModeResponse(RadioResponseInfo responseInfo, int mode) {
-        RadioResponse.responseInts(RIL.VOICE_SERVICE, mRil, responseInfo, mode);
+        RadioResponse.responseInts(HAL_SERVICE_VOICE, mRil, responseInfo, mode);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void handleStkCallSetupRequestFromSimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void hangupConnectionResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void hangupForegroundResumeBackgroundResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void hangupWaitingOrBackgroundResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
@@ -269,7 +271,7 @@
      * @param enable true for "vonr enabled" and false for "vonr disabled"
      */
     public void isVoNrEnabledResponse(RadioResponseInfo responseInfo, boolean enable) {
-        RILRequest rr = mRil.processResponse(RIL.VOICE_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_VOICE, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -283,112 +285,112 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void rejectCallResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendBurstDtmfResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendCdmaFeatureCodeResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendDtmfResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendUssdResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void separateConnectionResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCallForwardResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCallWaitingResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setClirResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setMuteResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setTtyModeResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setVoNrEnabledResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startDtmfResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void stopDtmfResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void switchWaitingOrHoldingAndActiveResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.VOICE_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_VOICE, mRil, responseInfo);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 3d21270..4447c07 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -40,6 +40,7 @@
     private ToneSettings mToneSettings = null;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private CallSettings mCallSettings = null;
+    private SMSSettings mSMSSettings =  null;
     private SetupEventListSettings mSetupEventListSettings = null;
     private boolean mLoadIconFailed = false;
 
@@ -61,6 +62,14 @@
         public TextMessage callMsg;
     }
 
+    /**
+     * Container for SEND SMS  command settings.
+     */
+    public class SMSSettings {
+        public TextMessage smsText;
+        public TextMessage destAddr;
+    }
+
     public class SetupEventListSettings {
         @UnsupportedAppUsage
         public int[] eventList;
@@ -84,57 +93,69 @@
         mCmdDet = cmdParams.mCmdDet;
         mLoadIconFailed =  cmdParams.mLoadIconFailed;
         switch(getCmdType()) {
-        case SET_UP_MENU:
-        case SELECT_ITEM:
-            mMenu = ((SelectItemParams) cmdParams).mMenu;
-            break;
-        case DISPLAY_TEXT:
-        case SET_UP_IDLE_MODE_TEXT:
-        case SEND_DTMF:
-        case SEND_SMS:
-        case REFRESH:
-        case RUN_AT:
-        case SEND_SS:
-        case SEND_USSD:
-            mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
-            break;
-        case GET_INPUT:
-        case GET_INKEY:
-            mInput = ((GetInputParams) cmdParams).mInput;
-            break;
-        case LAUNCH_BROWSER:
-            mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg;
-            mBrowserSettings = new BrowserSettings();
-            mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl;
-            mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode;
-            break;
-        case PLAY_TONE:
-            PlayToneParams params = (PlayToneParams) cmdParams;
-            mToneSettings = params.mSettings;
-            mTextMsg = params.mTextMsg;
-            break;
-        case GET_CHANNEL_STATUS:
-            mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
-            break;
-        case SET_UP_CALL:
-            mCallSettings = new CallSettings();
-            mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
-            mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg;
-            break;
-        case OPEN_CHANNEL:
-        case CLOSE_CHANNEL:
-        case RECEIVE_DATA:
-        case SEND_DATA:
-            BIPClientParams param = (BIPClientParams) cmdParams;
-            mTextMsg = param.mTextMsg;
-            break;
-        case SET_UP_EVENT_LIST:
-            mSetupEventListSettings = new SetupEventListSettings();
-            mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
-            break;
-        case PROVIDE_LOCAL_INFORMATION:
-        default:
-            break;
+            case SET_UP_MENU:
+            case SELECT_ITEM:
+                mMenu = ((SelectItemParams) cmdParams).mMenu;
+                break;
+            case SEND_SMS:
+                /* If cmdParams  is an instanceof SendSMSParams , then it means config value
+                 * config_stk_sms_send_support is true and the SMS should be sent by framework
+                 */
+                if (cmdParams instanceof SendSMSParams) {
+                    mSMSSettings = new SMSSettings();
+                    mSMSSettings.smsText = ((SendSMSParams) cmdParams).mTextSmsMsg;
+                    mSMSSettings.destAddr = ((SendSMSParams) cmdParams).mDestAddress;
+                    mTextMsg = ((SendSMSParams) cmdParams).mDisplayText.mTextMsg;
+                } else {
+                    mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
+                }
+                break;
+            case DISPLAY_TEXT:
+            case SET_UP_IDLE_MODE_TEXT:
+            case SEND_DTMF:
+            case REFRESH:
+            case RUN_AT:
+            case SEND_SS:
+            case SEND_USSD:
+                mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
+                break;
+            case GET_INPUT:
+            case GET_INKEY:
+                mInput = ((GetInputParams) cmdParams).mInput;
+                break;
+            case LAUNCH_BROWSER:
+                mTextMsg = ((LaunchBrowserParams) cmdParams).mConfirmMsg;
+                mBrowserSettings = new BrowserSettings();
+                mBrowserSettings.url = ((LaunchBrowserParams) cmdParams).mUrl;
+                mBrowserSettings.mode = ((LaunchBrowserParams) cmdParams).mMode;
+                break;
+            case PLAY_TONE:
+                PlayToneParams params = (PlayToneParams) cmdParams;
+                mToneSettings = params.mSettings;
+                mTextMsg = params.mTextMsg;
+                break;
+            case GET_CHANNEL_STATUS:
+                mTextMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
+                break;
+            case SET_UP_CALL:
+                mCallSettings = new CallSettings();
+                mCallSettings.confirmMsg = ((CallSetupParams) cmdParams).mConfirmMsg;
+                mCallSettings.callMsg = ((CallSetupParams) cmdParams).mCallMsg;
+                break;
+            case OPEN_CHANNEL:
+            case CLOSE_CHANNEL:
+            case RECEIVE_DATA:
+            case SEND_DATA:
+                BIPClientParams param = (BIPClientParams) cmdParams;
+                mTextMsg = param.mTextMsg;
+                break;
+            case SET_UP_EVENT_LIST:
+                mSetupEventListSettings = new SetupEventListSettings();
+                mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
+                break;
+            case PROVIDE_LOCAL_INFORMATION:
+            default:
+                break;
         }
     }
 
@@ -145,29 +166,34 @@
         mInput = in.readParcelable(Input.class.getClassLoader());
         mLoadIconFailed = (in.readByte() == 1);
         switch (getCmdType()) {
-        case LAUNCH_BROWSER:
-            mBrowserSettings = new BrowserSettings();
-            mBrowserSettings.url = in.readString();
-            mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()];
-            break;
-        case PLAY_TONE:
-            mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader());
-            break;
-        case SET_UP_CALL:
-            mCallSettings = new CallSettings();
-            mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader());
-            mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader());
-            break;
-        case SET_UP_EVENT_LIST:
-            mSetupEventListSettings = new SetupEventListSettings();
-            int length = in.readInt();
-            mSetupEventListSettings.eventList = new int[length];
-            for (int i = 0; i < length; i++) {
-                mSetupEventListSettings.eventList[i] = in.readInt();
-            }
-            break;
-        default:
-            break;
+            case LAUNCH_BROWSER:
+                mBrowserSettings = new BrowserSettings();
+                mBrowserSettings.url = in.readString();
+                mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()];
+                break;
+            case PLAY_TONE:
+                mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader());
+                break;
+            case SET_UP_CALL:
+                mCallSettings = new CallSettings();
+                mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader());
+                mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader());
+                break;
+            case SET_UP_EVENT_LIST:
+                mSetupEventListSettings = new SetupEventListSettings();
+                int length = in.readInt();
+                mSetupEventListSettings.eventList = new int[length];
+                for (int i = 0; i < length; i++) {
+                    mSetupEventListSettings.eventList[i] = in.readInt();
+                }
+                break;
+            case SEND_SMS:
+                mSMSSettings = new SMSSettings();
+                mSMSSettings.smsText = in.readParcelable(SendSMSParams.class.getClassLoader());
+                mSMSSettings.destAddr = in.readParcelable(SendSMSParams.class.getClassLoader());
+                break;
+            default:
+                break;
         }
     }
 
@@ -178,23 +204,29 @@
         dest.writeParcelable(mMenu, 0);
         dest.writeParcelable(mInput, 0);
         dest.writeByte((byte) (mLoadIconFailed ? 1 : 0));
-        switch(getCmdType()) {
-        case LAUNCH_BROWSER:
-            dest.writeString(mBrowserSettings.url);
-            dest.writeInt(mBrowserSettings.mode.ordinal());
-            break;
-        case PLAY_TONE:
-            dest.writeParcelable(mToneSettings, 0);
-            break;
-        case SET_UP_CALL:
-            dest.writeParcelable(mCallSettings.confirmMsg, 0);
-            dest.writeParcelable(mCallSettings.callMsg, 0);
-            break;
-        case SET_UP_EVENT_LIST:
-            dest.writeIntArray(mSetupEventListSettings.eventList);
-            break;
-        default:
-            break;
+        switch (getCmdType()) {
+            case LAUNCH_BROWSER:
+                dest.writeString(mBrowserSettings.url);
+                dest.writeInt(mBrowserSettings.mode.ordinal());
+                break;
+            case PLAY_TONE:
+                dest.writeParcelable(mToneSettings, 0);
+                break;
+            case SET_UP_CALL:
+                dest.writeParcelable(mCallSettings.confirmMsg, 0);
+                dest.writeParcelable(mCallSettings.callMsg, 0);
+                break;
+            case SET_UP_EVENT_LIST:
+                dest.writeIntArray(mSetupEventListSettings.eventList);
+                break;
+            case SEND_SMS:
+                if (mSMSSettings != null) {
+                    dest.writeParcelable(mSMSSettings.smsText, 0);
+                    dest.writeParcelable(mSMSSettings.destAddr, 0);
+                }
+                break;
+            default:
+                break;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 9ed5eb9..f0ac9af 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -20,24 +20,34 @@
 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT;
 
+import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.PendingIntent;
 import android.app.backup.BackupManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources.NotFoundException;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ProxyController;
+import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -138,12 +148,21 @@
 
     static final String STK_DEFAULT = "Default Message";
 
+    private static final String SMS_DELIVERY_ACTION =
+            "com.android.internal.telephony.cat.SMS_DELIVERY_ACTION";
+    private static final String SMS_SENT_ACTION =
+            "com.android.internal.telephony.cat.SMS_SENT_ACTION";
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int mSlotId;
+    private static HandlerThread sCatServiceThread;
 
     /* For multisim catservice should not be singleton */
     private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
-            Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId) {
+            Context context, IccFileHandler fh, UiccProfile uiccProfile, int slotId,
+            Looper looper) {
+        //creating new thread to avoid deadlock conditions with the framework thread.
+        super(looper);
         if (ci == null || ca == null || ir == null || context == null || fh == null
                 || uiccProfile == null) {
             throw new NullPointerException(
@@ -187,6 +206,10 @@
 
         CatLog.d(this, "Running CAT service on Slotid: " + mSlotId +
                 ". STK app installed:" + mStkAppInstalled);
+
+        SmsBroadcastReceiver smsBroadcastReceiver = new SmsBroadcastReceiver();
+        mContext.registerReceiver(smsBroadcastReceiver, new IntentFilter(SMS_DELIVERY_ACTION));
+        mContext.registerReceiver(smsBroadcastReceiver, new IntentFilter(SMS_SENT_ACTION));
     }
 
     /**
@@ -200,6 +223,10 @@
      */
     public static CatService getInstance(CommandsInterface ci,
             Context context, UiccProfile uiccProfile, int slotId) {
+        if (sCatServiceThread == null) {
+            sCatServiceThread = new HandlerThread("CatServiceThread");
+            sCatServiceThread.start();
+        }
         UiccCardApplication ca = null;
         IccFileHandler fh = null;
         IccRecords ir = null;
@@ -227,8 +254,8 @@
                         || uiccProfile == null) {
                     return null;
                 }
-
-                sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId);
+                sInstance[slotId] = new CatService(ci, ca, ir, context, fh, uiccProfile, slotId,
+                        sCatServiceThread.getLooper());
             } else if ((ir != null) && (mIccRecords != ir)) {
                 if (mIccRecords != null) {
                     mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]);
@@ -447,8 +474,49 @@
                     ((DisplayTextParams)cmdParams).mTextMsg.text = null;
                 }
                 break;
-            case SEND_DTMF:
             case SEND_SMS:
+                /* If cmdParams  is an instanceof SendSMSParams , then it means config value
+                 * config_stk_sms_send_support is true and the SMS should be sent by framework
+                 */
+                if (cmdParams instanceof SendSMSParams) {
+                    String text = null, destAddr = null;
+                    if (((SendSMSParams) cmdParams).mTextSmsMsg != null) {
+                        text = ((SendSMSParams) cmdParams).mTextSmsMsg.text;
+                    }
+                    if (((SendSMSParams) cmdParams).mDestAddress != null) {
+                        destAddr = ((SendSMSParams) cmdParams).mDestAddress.text;
+                    }
+                    if (text != null && destAddr != null) {
+                        ProxyController proxyController = ProxyController.getInstance(mContext);
+                        SubscriptionManager subscriptionManager = (SubscriptionManager)
+                                mContext.getSystemService(
+                                        Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+                        SubscriptionInfo subInfo =
+                                subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
+                                        mSlotId);
+                        if (subInfo != null) {
+                            sendStkSms(text, destAddr, subInfo.getSubscriptionId(), cmdParams,
+                                    proxyController);
+                        } else {
+                            sendTerminalResponse(cmdParams.mCmdDet,
+                                    ResultCode.CMD_DATA_NOT_UNDERSTOOD, false, 0x00, null);
+                            CatLog.d(this, "Subscription info is null");
+                        }
+                    } else {
+                        sendTerminalResponse(cmdParams.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
+                                false, 0x00, null);
+                        CatLog.d(this, "Sms text or Destination Address is null");
+                    }
+                } else {
+                    if ((((DisplayTextParams) cmdParams).mTextMsg.text != null)
+                            && (((DisplayTextParams) cmdParams).mTextMsg.text.equals(
+                            STK_DEFAULT))) {
+                        message = mContext.getText(com.android.internal.R.string.sending);
+                        ((DisplayTextParams) cmdParams).mTextMsg.text = message.toString();
+                    }
+                }
+                break;
+            case SEND_DTMF:
             case SEND_SS:
             case SEND_USSD:
                 if ((((DisplayTextParams)cmdParams).mTextMsg.text != null)
@@ -536,6 +604,61 @@
         broadcastCatCmdIntent(cmdMsg);
     }
 
+    /**
+     * Used to send STK based sms via CATService
+     * @param text The message body
+     * @param destAddr The destination Address
+     * @param subId Subscription Id
+     * @param cmdParams Send SMS Command Params
+     * @param proxyController ProxyController
+     * @hide
+     */
+    public void sendStkSms(String text, String destAddr, int subId, CommandParams cmdParams,
+            ProxyController proxyController) {
+        PendingIntent sentPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(SMS_SENT_ACTION).putExtra("cmdDetails",
+                        cmdParams.mCmdDet), PendingIntent.FLAG_MUTABLE);
+        PendingIntent deliveryPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(SMS_DELIVERY_ACTION).putExtra("cmdDetails",
+                        cmdParams.mCmdDet), PendingIntent.FLAG_MUTABLE);
+        SmsController smsController = proxyController.getSmsController();
+        smsController.sendTextForSubscriber(subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), destAddr, null, text, sentPendingIntent,
+                deliveryPendingIntent, false, 0L, true, true);
+    }
+
+    private class SmsBroadcastReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            CommandDetails commandDetails = (CommandDetails) intent.getExtra("cmdDetails");
+            if (intent.getAction().equals(SMS_SENT_ACTION)) {
+                int resultCode = getResultCode();
+                switch (resultCode) {
+                    case Activity.RESULT_OK:
+                        break;
+                    default:
+                        //ToDO handle Error cases bug : b/243123292
+                        CatLog.d(this, "Error sending STK SMS : " + resultCode);
+                        sendTerminalResponse(commandDetails, ResultCode.SMS_RP_ERROR, true,
+                                ResultCode.NETWORK_CRNTLY_UNABLE_TO_PROCESS.value(), null);
+                }
+            }
+            if (intent.getAction().equals(SMS_DELIVERY_ACTION)) {
+                int resultCode = getResultCode();
+                switch (resultCode) {
+                    case Activity.RESULT_OK:
+                        sendTerminalResponse(commandDetails, ResultCode.OK, false, 0, null);
+                        break;
+                    default:
+                        //ToDO handle Error cases bug: b/243123292
+                        CatLog.d(this, "Error delivering STK SMS : " + resultCode);
+                        sendTerminalResponse(commandDetails, ResultCode.SMS_RP_ERROR, true,
+                                ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS.value(), null);
+                }
+            }
+        }
+    }
 
     private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) {
         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
diff --git a/src/java/com/android/internal/telephony/cat/CommandParams.java b/src/java/com/android/internal/telephony/cat/CommandParams.java
index b9de4d1..8530ee2 100755
--- a/src/java/com/android/internal/telephony/cat/CommandParams.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParams.java
@@ -22,16 +22,16 @@
 
 /**
  * Container class for proactive command parameters.
- *
+ * @hide
  */
-class CommandParams {
+public class CommandParams {
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     CommandDetails mCmdDet;
     // Variable to track if an optional icon load has failed.
     boolean mLoadIconFailed = false;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    CommandParams(CommandDetails cmdDet) {
+    public CommandParams(CommandDetails cmdDet) {
         mCmdDet = cmdDet;
     }
 
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 7fbebfa..65f3c4a 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -29,6 +29,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.telephony.SmsMessage;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
@@ -40,9 +41,9 @@
 /**
  * Factory class, used for decoding raw byte arrays, received from baseband,
  * into a CommandParams object.
- *
+ * @hide
  */
-class CommandParamsFactory extends Handler {
+public class CommandParamsFactory extends Handler {
     private static CommandParamsFactory sInstance = null;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private IconLoader mIconLoader;
@@ -53,6 +54,7 @@
     private String mSavedLanguage;
     private String mRequestedLanguage;
     private boolean mNoAlphaUsrCnf = false;
+    private boolean mStkSmsSendViaTelephony = false;
 
     // constants
     static final int MSG_ID_LOAD_ICON_DONE = 1;
@@ -86,7 +88,15 @@
     private static final int MAX_GSM7_DEFAULT_CHARS = 239;
     private static final int MAX_UCS2_CHARS = 118;
 
-    static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
+    /**
+     * Returns a singleton instance of CommandParamsFactory
+     * @param caller Class used for queuing raw ril messages, decoding them into
+     *               CommandParams objects and sending the result back to the CAT Service.
+     * @param fh IccFileHandler Object
+     * @param context The Context
+     * @return CommandParamsFactory instance
+     */
+    public static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
             IccFileHandler fh, Context context) {
         if (sInstance != null) {
             return sInstance;
@@ -106,6 +116,12 @@
         } catch (NotFoundException e) {
             mNoAlphaUsrCnf = false;
         }
+        try {
+            mStkSmsSendViaTelephony = context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_stk_sms_send_support);
+        } catch (NotFoundException e) {
+            mStkSmsSendViaTelephony = false;
+        }
     }
 
     private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) {
@@ -187,8 +203,14 @@
                 case GET_INPUT:
                     cmdPending = processGetInput(cmdDet, ctlvs);
                     break;
-                case SEND_DTMF:
                 case SEND_SMS:
+                    if (mStkSmsSendViaTelephony) {
+                        cmdPending = processSMSEventNotify(cmdDet, ctlvs);
+                    } else {
+                        cmdPending = processEventNotify(cmdDet, ctlvs);
+                    }
+                    break;
+                case SEND_DTMF:
                 case REFRESH:
                 case RUN_AT:
                 case SEND_SS:
@@ -735,6 +757,62 @@
         return false;
     }
 
+
+    /**
+     * Processes SMS_EVENT_NOTIFY message from baseband.
+     *
+     * Method extracts values such as Alpha Id,Icon Id,Sms Tpdu etc from the ComprehensionTlv,
+     * in order to create the CommandParams i.e. SendSMSParams.
+     *
+     * @param cmdDet Command Details container object.
+     * @param ctlvs List of ComprehensionTlv objects following Command Details
+     *        object and Device Identities object within the proactive command
+     * @return true if the command is processing is pending and additional
+     *         asynchronous processing is required.
+     * @hide
+     */
+    public boolean processSMSEventNotify(CommandDetails cmdDet,
+            List<ComprehensionTlv> ctlvs) throws ResultException {
+        CatLog.d(this, "processSMSEventNotify");
+
+        TextMessage textMsg = new TextMessage();
+        IconId iconId = null;
+
+        ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
+                ctlvs);
+        /* Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV object.
+         *
+         * String corresponding to the alpha identifier is obtained and saved as part of
+         * the DisplayTextParams.
+         */
+        textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
+
+        ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
+        if (ctlv != null) {
+            // Retrieves icon id from the Icon Identifier COMPREHENSION-TLV object
+            iconId = ValueParser.retrieveIconId(ctlv);
+            textMsg.iconSelfExplanatory = iconId.selfExplanatory;
+        }
+
+        textMsg.responseNeeded = false;
+        DisplayTextParams displayTextParams = new DisplayTextParams(cmdDet, textMsg);
+        ComprehensionTlv ctlvTpdu = searchForTag(ComprehensionTlvTag.SMS_TPDU,
+                ctlvs);
+        // Retrieves smsMessage from the SMS TPDU COMPREHENSION-TLV object
+        SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlvTpdu);
+        if (smsMessage != null) {
+            TextMessage smsText = new TextMessage();
+            // Obtains the sms message content.
+            smsText.text = smsMessage.getMessageBody();
+            TextMessage destAddr = new TextMessage();
+            // Obtains the destination Address.
+            destAddr.text = smsMessage.getRecipientAddress();
+            mCmdParams = new SendSMSParams(cmdDet, smsText, destAddr, displayTextParams);
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Processes SET_UP_EVENT_LIST proactive command from the SIM card.
      *
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index 5542b65..416c669 100644
--- a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -50,7 +50,7 @@
      * @param data Byte array containing the value
      * @param valueIndex Index in data at which the value starts
      */
-    protected ComprehensionTlv(int tag, boolean cr, int length, byte[] data,
+    public ComprehensionTlv(int tag, boolean cr, int length, byte[] data,
             int valueIndex) {
         mTag = tag;
         mCr = cr;
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
index c25b59e..4b10cae 100755
--- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -32,8 +32,9 @@
 /**
  * Class used for queuing raw ril messages, decoding them into CommanParams
  * objects and sending the result back to the CAT Service.
+ * @hide
  */
-class RilMessageDecoder extends StateMachine {
+public class RilMessageDecoder extends StateMachine {
 
     // constants
     private static final int CMD_START = 1;
diff --git a/src/java/com/android/internal/telephony/cat/SendSMSParams.java b/src/java/com/android/internal/telephony/cat/SendSMSParams.java
new file mode 100644
index 0000000..f5108f0
--- /dev/null
+++ b/src/java/com/android/internal/telephony/cat/SendSMSParams.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.cat;
+
+class SendSMSParams extends CommandParams {
+
+    TextMessage mTextSmsMsg;
+    TextMessage mDestAddress;
+    DisplayTextParams mDisplayText;
+
+    SendSMSParams(CommandDetails cmdDet, TextMessage textMsg, TextMessage destAddress,
+            DisplayTextParams displayText) {
+        super(cmdDet);
+        mTextSmsMsg = textMsg;
+        mDestAddress = destAddress;
+        mDisplayText = displayText;
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/cat/ValueParser.java b/src/java/com/android/internal/telephony/cat/ValueParser.java
index 7c09136..bd17f48 100644
--- a/src/java/com/android/internal/telephony/cat/ValueParser.java
+++ b/src/java/com/android/internal/telephony/cat/ValueParser.java
@@ -18,16 +18,24 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.telephony.SmsMessage;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.cat.Duration.TimeUnit;
 import com.android.internal.telephony.uicc.IccUtils;
 
+import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
-abstract class ValueParser {
+
+/**
+ *  Util class that parses different entities from the ctlvs ComprehensionTlv List
+ * @hide
+ */
+public abstract class ValueParser {
 
     /**
      * Search for a Command Details object from a list.
@@ -352,4 +360,42 @@
             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
         }
     }
+
+    /**
+     * Retrieve's the tpdu from the ctlv and creates the SmsMessage from pdu.
+     * @param ctlv  ComprehensionTlv value
+     * @return message SmsMessage to retrieve the destAddress and Text
+     * @throws ResultException
+     * @hide
+     */
+    public static SmsMessage retrieveTpduAsSmsMessage(ComprehensionTlv ctlv)
+            throws ResultException {
+        if (ctlv != null) {
+            byte[] rawValue = ctlv.getRawValue();
+            int valueIndex = ctlv.getValueIndex();
+            int length = ctlv.getLength();
+            if (length != 0) {
+                try {
+                    byte[] pdu = Arrays.copyOfRange(rawValue, valueIndex, (valueIndex + length));
+                    ByteArrayOutputStream bo = new ByteArrayOutputStream(pdu.length + 1);
+                    /* Framework's TPdu Parser expects the TPdu be prepended with SC-Address.
+                     * else the parser will throw an exception. So prepending TPdu with 0,
+                     * which indicates that there is no SC address and its length is 0.
+                     * This way Parser will skip parsing for SC-Address
+                     */
+                    bo.write(0x00);
+                    bo.write(pdu, 0, pdu.length);
+                    byte[] frameworkPdu = bo.toByteArray();
+                    //ToDO handle for 3GPP2 format bug: b/243123533
+                    SmsMessage message = SmsMessage.createFromPdu(frameworkPdu,
+                            SmsMessage.FORMAT_3GPP);
+                    return message;
+                } catch (IndexOutOfBoundsException e) {
+                    throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
+                }
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 5a830a8..ebc6342 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -93,6 +93,21 @@
     }
 
     @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+            int validityPeriod, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, message,
+                statusReportRequested, smsHeader, priority);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, destPort, message,
+                statusReportRequested);
+    }
+
+    @Override
     protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
         return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly);
     }
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index d3af323..66996f4 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.data;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -58,7 +60,6 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.SlidingWindowEventCounter;
-import com.android.internal.telephony.dataconnection.DataThrottler;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -67,7 +68,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -156,8 +156,6 @@
 
     private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList();
 
-    private final Set<DataThrottler> mDataThrottlers = new HashSet<>();
-
     private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -175,19 +173,9 @@
     };
 
     /**
-     * The current transport of the APN type. The key is the APN type, and the value is the
-     * transport.
-     */
-    private final Map<Integer, Integer> mCurrentTransports = new ConcurrentHashMap<>();
-
-    /**
      * The preferred transport of the APN type. The key is the APN type, and the value is the
-     * transport. The preferred transports are updated as soon as QNS changes the preference, while
-     * the current transports are updated after handover complete.
+     * transport. The preferred transports are updated as soon as QNS changes the preference.
      */
-    // TODO: Deprecate mPreferredTransports. Should expose mAvailableNetworks to
-    //  DataNetworkController after we support multi preferred access networks (i.e.
-    //  DataNetworkController might select 2nd preferred access network in some scenarios.)
     private final Map<Integer, Integer> mPreferredTransports = new ConcurrentHashMap<>();
 
     /**
@@ -197,21 +185,6 @@
             new ArraySet<>();
 
     /**
-     * Registers the data throttler in order to receive APN status changes.
-     *
-     * @param dataThrottler the data throttler to register
-     */
-    public void registerDataThrottler(DataThrottler dataThrottler) {
-        this.post(() -> {
-            QualifiedNetworksServiceConnection serviceConnection = mServiceConnection;
-            this.mDataThrottlers.add(dataThrottler);
-            if (serviceConnection != null) {
-                serviceConnection.registerDataThrottler(dataThrottler);
-            }
-        });
-    }
-
-    /**
      * Represents qualified network types list on a specific APN type.
      */
     public static class QualifiedNetworks {
@@ -252,18 +225,6 @@
     }
 
     private final class QualifiedNetworksServiceConnection implements ServiceConnection {
-
-        /**
-         * The APN throttle status callback is attached to the service connection so that they have
-         * the same life cycle.
-         */
-        @NonNull
-        private final ThrottleStatusChangedCallback mThrottleStatusCallback;
-
-        QualifiedNetworksServiceConnection() {
-            mThrottleStatusCallback = new ThrottleStatusChangedCallback();
-        }
-
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) log("onServiceConnected " + name);
@@ -275,9 +236,6 @@
                 service.linkToDeath(mDeathRecipient, 0 /* flags */);
                 mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(),
                         new QualifiedNetworksServiceCallback());
-
-                registerDataThrottlersFirstTime();
-
             } catch (RemoteException e) {
                 loge("Remote exception. " + e);
             }
@@ -286,55 +244,9 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DBG) log("onServiceDisconnected " + name);
-            unregisterForThrottleCallbacks();
             mTargetBindingPackageName = null;
         }
 
-        /**
-         * Runs on all of the data throttlers when the service is connected
-         */
-        private void registerDataThrottlersFirstTime() {
-            post(() -> {
-                for (DataThrottler dataThrottler : mDataThrottlers) {
-                    dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
-                }
-            });
-        }
-
-        private void registerDataThrottler(DataThrottler dataThrottler) {
-            post(() -> {
-                dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
-            });
-        }
-
-        private void unregisterForThrottleCallbacks() {
-            post(() -> {
-                for (DataThrottler dataThrottler : mDataThrottlers) {
-                    dataThrottler.unregisterForThrottleStatusChanges(mThrottleStatusCallback);
-                }
-            });
-        }
-    }
-
-    private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
-        @Override
-        public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
-            post(() -> {
-                try {
-                    List<ThrottleStatus> throttleStatusesBySlot =
-                            throttleStatuses
-                                    .stream()
-                                    .filter(x -> x.getSlotIndex() == mPhone.getPhoneId())
-                                    .collect(Collectors.toList());
-                    if (mIQualifiedNetworksService != null) {
-                        mIQualifiedNetworksService.reportThrottleStatusChanged(mPhone.getPhoneId(),
-                                throttleStatusesBySlot);
-                    }
-                } catch (Exception ex) {
-                    loge("onThrottleStatusChanged", ex);
-                }
-            });
-        }
     }
 
     private final class QualifiedNetworksServiceCallback extends
@@ -479,11 +391,10 @@
             bindQualifiedNetworksService();
         }
 
-        if (phone.isUsingNewDataStack()) {
-            // Using post to delay the registering because data retry manager and data config
-            // manager instances are created later than access networks manager.
-            post(() -> {
-                mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
+        // Using post to delay the registering because data retry manager and data config
+        // manager instances are created later than access networks manager.
+        post(() -> {
+            mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
                     new DataRetryManager.DataRetryManagerCallback(this::post) {
                         @Override
                         public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
@@ -498,16 +409,15 @@
                             }
                         }
                     });
-                mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager();
-                mDataConfigManager.registerCallback(
-                        new DataConfigManager.DataConfigManagerCallback(this::post) {
-                            @Override
-                            public void onDeviceConfigChanged() {
-                                mApnTypeToQnsChangeNetworkCounter.clear();
-                            }
-                        });
-            });
-        }
+            mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager();
+            mDataConfigManager.registerCallback(
+                    new DataConfigManager.DataConfigManagerCallback(this::post) {
+                        @Override
+                        public void onDeviceConfigChanged() {
+                            mApnTypeToQnsChangeNetworkCounter.clear();
+                        }
+                    });
+        });
     }
 
     /**
@@ -674,7 +584,7 @@
             return true;
         }
 
-        return mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+        return mPhone.getHalVersion(HAL_SERVICE_DATA).less(RIL.RADIO_HAL_VERSION_1_4);
     }
 
     /**
@@ -687,60 +597,6 @@
         return mAvailableTransports;
     }
 
-    /**
-     * Get the transport based on the network capability.
-     *
-     * @param netCap The network capability.
-     * @return The transport type.
-     */
-    public @TransportType int getCurrentTransportByNetworkCapability(@NetCapability int netCap) {
-        return getCurrentTransport(DataUtils.networkCapabilityToApnType(netCap));
-    }
-
-    /**
-     * Get the transport based on the APN type.
-     *
-     * @param apnType APN type
-     * @return The transport type
-     */
-    // TODO: Remove this after TransportManager is removed.
-    public @TransportType int getCurrentTransport(@ApnType int apnType) {
-        // In legacy mode, always route to cellular.
-        if (isInLegacyMode()) {
-            return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-        }
-
-        // If we can't find the corresponding transport, always route to cellular.
-        return mCurrentTransports.get(apnType) == null
-                ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mCurrentTransports.get(apnType);
-    }
-
-    /**
-     * Set the current transport of a network capability.
-     *
-     * @param netCap The network capability.
-     * @param transport The transport.
-     */
-    public void setCurrentTransportByNetworkCapability(@NetCapability int netCap,
-            @TransportType int transport) {
-        setCurrentTransport(DataUtils.networkCapabilityToApnType(netCap), transport);
-    }
-
-    /**
-     * Set the current transport of apn type.
-     *
-     * @param apnType The APN type
-     * @param transport The transport.
-     */
-    // TODO: Remove this after TransportManager is removed.
-    public void setCurrentTransport(@ApnType int apnType, @TransportType int transport) {
-        Integer previousTransport = mCurrentTransports.put(apnType, transport);
-        if (previousTransport == null || previousTransport != transport) {
-            logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
-                    + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
-        }
-    }
-
     private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
         return accessNetwork == AccessNetworkType.IWLAN
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
@@ -799,20 +655,14 @@
     }
 
     /**
-     * Check if there is any APN type's current transport is on IWLAN.
+     * Check if there is any APN type preferred on IWLAN.
      *
      * @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}.
      */
     public boolean isAnyApnOnIwlan() {
         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
-            if (mPhone.isUsingNewDataStack()) {
-                if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                    return true;
-                }
-            } else {
-                if (getCurrentTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                    return true;
-                }
+            if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+                return true;
             }
         }
         return false;
@@ -876,14 +726,6 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println(AccessNetworksManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":");
         pw.increaseIndent();
-        pw.println("current transports=");
-        pw.increaseIndent();
-        for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
-            pw.println(ApnSetting.getApnTypeString(apnType)
-                    + ": " + AccessNetworkConstants.transportTypeToString(
-                    getCurrentTransport(apnType)));
-        }
-        pw.decreaseIndent();
         pw.println("preferred transports=");
         pw.increaseIndent();
         for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
diff --git a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
index c63676f..6d020f7 100644
--- a/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/data/CellularNetworkValidator.java
@@ -17,7 +17,7 @@
 package com.android.internal.telephony.data;
 
 import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG;
+import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
 
 import android.content.Context;
@@ -192,7 +192,7 @@
             if (configManager != null) {
                 PersistableBundle b = configManager.getConfigForSubId(subId);
                 if (b != null) {
-                    ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG);
+                    ttl = b.getLong(KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG);
                 }
             }
             // Ttl can't be bigger than one day for now.
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 73f08b9..81bf25f 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -76,6 +76,12 @@
     /** The default timeout in ms for data network stuck in a transit state. */
     private static final int DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS = 300000;
 
+    /** Default time threshold in ms to define a internet connection status to be stable. */
+    public static int DEFAULT_AUTO_DATA_SWITCH_STABILITY_TIME_MS = 10000;
+
+    /** The max number of retries when a pre-switching validation fails. */
+    public static int DEFAULT_AUTO_DATA_SWITCH_MAX_RETRY = 7;
+
     /** Event for carrier config changed. */
     private static final int EVENT_CARRIER_CONFIG_CHANGED = 1;
 
@@ -135,8 +141,7 @@
     private static final String DATA_CONFIG_NETWORK_TYPE_IDEN = "iDEN";
 
     /** Network type LTE. Should not be used outside of DataConfigManager. */
-    // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
-    public static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE";
+    private static final String DATA_CONFIG_NETWORK_TYPE_LTE = "LTE";
 
     /** Network type HSPA+. Should not be used outside of DataConfigManager. */
     private static final String DATA_CONFIG_NETWORK_TYPE_HSPAP = "HSPA+";
@@ -154,12 +159,10 @@
     private static final String DATA_CONFIG_NETWORK_TYPE_LTE_CA = "LTE_CA";
 
     /** Network type NR_NSA. Should not be used outside of DataConfigManager. */
-    // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
-    public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA";
+    private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA = "NR_NSA";
 
     /** Network type NR_NSA_MMWAVE. Should not be used outside of DataConfigManager. */
-    // TODO: Public only for use by DcTracker. This should be private once DcTracker is removed.
-    public static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
+    private static final String DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
 
     /** Network type NR_SA. Should not be used outside of DataConfigManager. */
     private static final String DATA_CONFIG_NETWORK_TYPE_NR_SA = "NR_SA";
@@ -221,6 +224,12 @@
             "anomaly_network_handover_timeout";
     /** DeviceConfig key of anomaly report: True for enabling APN config invalidity detection */
     private static final String KEY_ANOMALY_APN_CONFIG_ENABLED = "anomaly_apn_config_enabled";
+    /** DeviceConfig key of the time threshold in ms for defining a network status to be stable. **/
+    private static final String KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD =
+            "auto_data_switch_availability_stability_time_threshold";
+    /** DeviceConfig key of the maximum number of retries when a validation for switching failed.**/
+    private static final String KEY_AUTO_DATA_SWITCH_VALIDATION_MAX_RETRY =
+            "auto_data_switch_validation_max_retry";
 
     /** Anomaly report thresholds for frequent setup data call failure. */
     private EventFrequency mSetupDataCallAnomalyReportThreshold;
@@ -263,6 +272,18 @@
      */
     private boolean mIsApnConfigAnomalyReportEnabled;
 
+    /**
+     * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
+     * in service, wifi is the default active network.etc), while -1 indicates auto switch feature
+     * disabled.
+     */
+    private long mAutoDataSwitchAvailabilityStabilityTimeThreshold;
+
+    /**
+     * The maximum number of retries when a pre-switching validation fails.
+     */
+    private int mAutoDataSwitchValidationMaxRetry;
+
     private @NonNull final Phone mPhone;
     private @NonNull final String mLogTag;
 
@@ -430,6 +451,12 @@
                 KEY_ANOMALY_NETWORK_HANDOVER_TIMEOUT, DEFAULT_NETWORK_TRANSIT_STATE_TIMEOUT_MS);
         mIsApnConfigAnomalyReportEnabled = properties.getBoolean(
                 KEY_ANOMALY_APN_CONFIG_ENABLED, false);
+        mAutoDataSwitchAvailabilityStabilityTimeThreshold = properties.getInt(
+                KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD,
+                DEFAULT_AUTO_DATA_SWITCH_STABILITY_TIME_MS);
+        mAutoDataSwitchValidationMaxRetry = properties.getInt(
+                KEY_AUTO_DATA_SWITCH_VALIDATION_MAX_RETRY,
+                DEFAULT_AUTO_DATA_SWITCH_MAX_RETRY);
     }
 
     /**
@@ -922,6 +949,22 @@
     }
 
     /**
+     * @return The maximum number of retries when a validation for switching failed.
+     */
+    public int getAutoDataSwitchValidationMaxRetry() {
+        return mAutoDataSwitchValidationMaxRetry;
+    }
+
+    /**
+     * @return Time threshold in ms to define a internet connection status to be stable
+     * (e.g. out of service, in service, wifi is the default active network.etc), while -1 indicates
+     * auto switch feature disabled.
+     */
+    public long getAutoDataSwitchAvailabilityStabilityTimeThreshold() {
+        return mAutoDataSwitchAvailabilityStabilityTimeThreshold;
+    }
+
+    /**
      * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}.
      * The config string will have the following form, with values in bytes:
      * "read_min,read_default,read_max,write_min,write_default,write_max"
@@ -1006,9 +1049,8 @@
      * @param displayInfo The {@link TelephonyDisplayInfo} used to determine the type.
      * @return The equivalent {@link DataConfigNetworkType}.
      */
-    public static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
+    private static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
             @NonNull TelephonyDisplayInfo displayInfo) {
-        // TODO: Make method private once DataConnection is removed
         int networkType = displayInfo.getNetworkType();
         switch (displayInfo.getOverrideNetworkType()) {
             case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
@@ -1302,6 +1344,9 @@
         pw.println("mNetworkDisconnectingTimeout=" + mNetworkDisconnectingTimeout);
         pw.println("mNetworkHandoverTimeout=" + mNetworkHandoverTimeout);
         pw.println("mIsApnConfigAnomalyReportEnabled=" + mIsApnConfigAnomalyReportEnabled);
+        pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
+                + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+        pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry);
         pw.println("Metered APN types=" + mMeteredApnTypes.stream()
                 .map(ApnSetting::getApnTypeString).collect(Collectors.joining(",")));
         pw.println("Roaming metered APN types=" + mRoamingMeteredApnTypes.stream()
diff --git a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java b/src/java/com/android/internal/telephony/data/DataEnabledOverride.java
deleted file mode 100644
index e639ba6..0000000
--- a/src/java/com/android/internal/telephony/data/DataEnabledOverride.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.telephony.Annotation.ApnType;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.data.DataEnabledOverride.OverrideConditions.Condition;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * This class represents the rules for overriding data enabled settings in different conditions.
- * When data is disabled by the user, data can still be turned on temporarily when conditions
- * satisfy any rule here.
- */
-public class DataEnabledOverride {
-
-    private final Set<OverrideRule> mRules = new HashSet<>();
-
-    /**
-     * The rule for allowing data during voice call.
-     */
-    private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL =
-            new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL
-                    | OverrideConditions.CONDITION_NON_DEFAULT
-                    | OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED
-                    | OverrideConditions.CONDITION_DSDS_ENABLED);
-
-    /**
-     * The rule for always allowing mms. Without adding any condition to the rule, any condition can
-     * satisfy this rule for mms.
-     */
-    private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS =
-            new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY);
-
-    /**
-     * Data enabled override rule
-     */
-    private static class OverrideRule {
-        /**
-         * APN type of the rule. The rule is APN type specific. The override is applicable to the
-         * specified APN type as well. For now we only support one APN type per rule. Can be
-         * expanded to multiple APN types in the future.
-         */
-        private final @ApnType int mApnType;
-
-        /** The required conditions for overriding */
-        private final OverrideConditions mRequiredConditions;
-
-        /**
-         * Constructor
-         *
-         * @param rule The override rule string. For example, {@code mms=nonDefault} or
-         * {@code default=voiceCall & nonDefault}
-         */
-        OverrideRule(@NonNull String rule) {
-            String[] tokens = rule.trim().split("\\s*=\\s*");
-            if (tokens.length != 2) {
-                throw new IllegalArgumentException("Invalid data enabled override rule format: "
-                        + rule);
-            }
-
-            if (TextUtils.isEmpty(tokens[0])) {
-                throw new IllegalArgumentException("APN type can't be empty");
-            }
-
-            mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]);
-            if (mApnType == ApnSetting.TYPE_NONE) {
-                throw new IllegalArgumentException("Invalid APN type. Rule=" + rule);
-            }
-
-            mRequiredConditions = new OverrideConditions(tokens[1]);
-        }
-
-        /**
-         * Constructor
-         *
-         * @param apnType APN type of the rule
-         * @param requiredConditions The required conditions for the rule
-         */
-        private OverrideRule(int apnType, int requiredConditions) {
-            mApnType = apnType;
-            mRequiredConditions = new OverrideConditions(requiredConditions);
-        }
-
-        /**
-         * Check if this rule can be satisfied by the given APN type and provided conditions.
-         *
-         * @param apnType APN type to check
-         * @param providedConditions The provided conditions to check
-         * @return {@code true} if satisfied
-         */
-        boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) {
-            return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL)
-                    && mRequiredConditions.allMet(providedConditions);
-        }
-
-        @Override
-        public String toString() {
-            return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            OverrideRule that = (OverrideRule) o;
-            return mApnType == that.mApnType
-                    && Objects.equals(mRequiredConditions, that.mRequiredConditions);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mApnType, mRequiredConditions);
-        }
-    }
-
-    /**
-     * Represent the conditions for overriding data enabled settings
-     */
-    static class OverrideConditions {
-        // Possible values for data enabled override condition. Note these flags are bitmasks.
-        /** Unconditionally override enabled settings */
-        static final int CONDITION_UNCONDITIONALLY = 0;
-
-        /** Enable data only on subscription that is not user selected default data subscription */
-        static final int CONDITION_NON_DEFAULT = 1 << 0;
-
-        /** Enable data only when device has ongoing voice call */
-        static final int CONDITION_IN_VOICE_CALL = 1 << 1;
-
-        /** Enable data only when default data is on */
-        static final int CONDITION_DEFAULT_DATA_ENABLED = 1 << 2;
-
-        /** Enable data only when device is in DSDS mode */
-        static final int CONDITION_DSDS_ENABLED = 1 << 3;
-
-        /** Enable data unconditionally in string format */
-        static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally";
-
-        /** Enable data only on subscription that is not default in string format */
-        static final String CONDITION_NON_DEFAULT_STRING = "nonDefault";
-
-        /** Enable data only when device has ongoing voice call in string format */
-        static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall";
-
-        /** Enable data only when default data is on in string format */
-        static final String CONDITION_DEFAULT_DATA_ENABLED_STRING = "DefaultDataOn";
-
-        /** Enable data only when device is in DSDS mode in string format */
-        static final String CONDITION_DSDS_ENABLED_STRING = "dsdsEnabled";
-
-        /** @hide */
-        @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = {
-                CONDITION_NON_DEFAULT,
-                CONDITION_IN_VOICE_CALL,
-                CONDITION_DEFAULT_DATA_ENABLED,
-                CONDITION_DSDS_ENABLED
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Condition {}
-
-        private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>();
-        private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>();
-
-        static {
-            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT,
-                    CONDITION_NON_DEFAULT_STRING);
-            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL,
-                    CONDITION_VOICE_CALL_STRING);
-            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DEFAULT_DATA_ENABLED,
-                    CONDITION_DEFAULT_DATA_ENABLED_STRING);
-            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_DSDS_ENABLED,
-                    CONDITION_DSDS_ENABLED_STRING);
-
-            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING,
-                    CONDITION_UNCONDITIONALLY);
-            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING,
-                    CONDITION_NON_DEFAULT);
-            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING,
-                    CONDITION_IN_VOICE_CALL);
-            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DEFAULT_DATA_ENABLED_STRING,
-                    CONDITION_DEFAULT_DATA_ENABLED);
-            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_DSDS_ENABLED_STRING,
-                    CONDITION_DSDS_ENABLED);
-        }
-
-        private final @Condition int mConditions;
-
-        /**
-         * Conditions for overriding data enabled setting
-         *
-         * @param conditions Conditions in string format
-         */
-        OverrideConditions(@NonNull String conditions) {
-            mConditions = getBitmaskFromString(conditions);
-        }
-
-        /**
-         * Conditions for overriding data enabled setting
-         *
-         * @param conditions Conditions in bitmask
-         */
-        OverrideConditions(@Condition int conditions) {
-            mConditions = conditions;
-        }
-
-        private static String getStringFromBitmask(@Condition int conditions) {
-            if (conditions == CONDITION_UNCONDITIONALLY) {
-                return CONDITION_UNCONDITIONALLY_STRING;
-            }
-            List<String> conditionsStrings = new ArrayList<>();
-            for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) {
-                if ((conditions & condition) == condition) {
-                    conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition));
-                }
-            }
-            return TextUtils.join("&", conditionsStrings);
-        }
-
-        private static @Condition int getBitmaskFromString(@NonNull String str) {
-            if (TextUtils.isEmpty(str)) {
-                throw new IllegalArgumentException("Empty rule string");
-            }
-
-            String[] conditionStrings = str.trim().split("\\s*&\\s*");
-            int bitmask = 0;
-
-            for (String conditionStr : conditionStrings) {
-                if (!TextUtils.isEmpty(conditionStr)) {
-                    if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) {
-                        throw new IllegalArgumentException("Invalid conditions: " + str);
-                    }
-                    bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr);
-                }
-            }
-
-            return bitmask;
-        }
-
-        /**
-         * Check if provided conditions can meet all conditions in the rule.
-         *
-         * @param providedConditions The provided conditions
-         * @return {@code true} if all conditions are met.
-         */
-        boolean allMet(@Condition int providedConditions) {
-            return (providedConditions & mConditions) == mConditions;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            OverrideConditions that = (OverrideConditions) o;
-            return mConditions == that.mConditions;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mConditions);
-        }
-
-        @Override
-        public String toString() {
-            return getStringFromBitmask(mConditions);
-        }
-    }
-
-    /**
-     * Constructor
-     *
-     * @param rules Data enabled override rules
-     */
-    public DataEnabledOverride(@NonNull String rules) {
-        updateRules(rules);
-    }
-
-    /**
-     * Update the data enabled override rules.
-     *
-     * @param newRules New override rules
-     */
-    @VisibleForTesting
-    public void updateRules(@NonNull String newRules) {
-        mRules.clear();
-        String[] rulesString = newRules.trim().split("\\s*,\\s*");
-        for (String rule : rulesString) {
-            if (!TextUtils.isEmpty(rule)) {
-                mRules.add(new OverrideRule(rule));
-            }
-        }
-    }
-
-    /**
-     * Set always allowing MMS
-     *
-     * @param allow {@code true} if always allowing, otherwise {@code false}.
-     */
-    public void setAlwaysAllowMms(boolean allow) {
-        if (allow) {
-            mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
-        } else {
-            mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
-        }
-    }
-
-    /**
-     * Set allowing mobile data during voice call. This is used for allowing data on the non-default
-     * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
-     * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
-     * non-default data SIM during the life cycle of the voice call.
-     *
-     * @param allow {@code true} if allowing using data during voice call, {@code false} if
-     * disallowed.
-     */
-    public void setDataAllowedInVoiceCall(boolean allow) {
-        if (allow) {
-            mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
-        } else {
-            mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
-        }
-    }
-
-    /**
-     * Check if data is allowed during voice call.
-     *
-     * @return {@code true} if data is allowed during voice call.
-     */
-    public boolean isDataAllowedInVoiceCall() {
-        return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
-    }
-
-    public boolean isMmsAlwaysAllowed() {
-        return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
-    }
-
-    private boolean canSatisfyAnyRule(@ApnType int apnType,
-                                      @Condition int providedConditions) {
-        for (OverrideRule rule : mRules) {
-            if (rule.isSatisfiedByConditions(apnType, providedConditions)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private @Condition int getCurrentConditions(Phone phone) {
-        int conditions = 0;
-
-        if (phone != null) {
-            // Check if the device is on voice call
-            if (phone.getState() != PhoneConstants.State.IDLE) {
-                conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL;
-            }
-
-            int defaultDataSubId = SubscriptionController.getInstance().getDefaultDataSubId();
-
-            if (phone.getSubId() != defaultDataSubId) {
-                conditions |= OverrideConditions.CONDITION_NON_DEFAULT;
-            }
-
-            if (defaultDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                int phoneId = SubscriptionController.getInstance().getPhoneId(defaultDataSubId);
-                try {
-                    Phone defaultDataPhone = PhoneFactory.getPhone(phoneId);
-                    if (defaultDataPhone != null && defaultDataPhone.isUserDataEnabled()) {
-                        conditions |= OverrideConditions.CONDITION_DEFAULT_DATA_ENABLED;
-                    }
-                } catch (IllegalStateException e) {
-                    //ignore the exception and do not add the condition
-                    Log.d("DataEnabledOverride", e.getMessage());
-                }
-            }
-
-            if (TelephonyManager.from(phone.getContext()).isMultiSimEnabled()) {
-                conditions |= OverrideConditions.CONDITION_DSDS_ENABLED;
-            }
-        }
-
-        return conditions;
-    }
-
-    /**
-     * Check for given APN type if we should enable data.
-     *
-     * @param phone Phone object
-     * @param apnType APN type
-     * @return {@code true} if data should be enabled for the current condition.
-     */
-    public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) {
-        return canSatisfyAnyRule(apnType, getCurrentConditions(phone));
-    }
-
-    /**
-     * Get data enabled override rules.
-     *
-     * @return Get data enabled override rules in string format
-     */
-    @NonNull
-    public String getRules() {
-        List<String> ruleStrings = new ArrayList<>();
-        for (OverrideRule rule : mRules) {
-            ruleStrings.add(rule.toString());
-        }
-        return TextUtils.join(",", ruleStrings);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        DataEnabledOverride that = (DataEnabledOverride) o;
-        return mRules.equals(that.mRules);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mRules);
-    }
-
-    @Override
-    public String toString() {
-        return "DataEnabledOverride: [rules=\"" + getRules() + "\"]";
-    }
-}
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 7616b93..dabe43f 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.data;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -1024,7 +1026,7 @@
             mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
                     getHandler(), EVENT_DISPLAY_INFO_CHANGED, null);
             mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
-                    EVENT_SERVICE_STATE_CHANGED);
+                    EVENT_SERVICE_STATE_CHANGED, null);
             for (int transport : mAccessNetworksManager.getAvailableTransports()) {
                 mDataServiceManagers.get(transport)
                         .registerForDataCallListChanged(getHandler(), EVENT_DATA_STATE_CHANGED);
@@ -1417,6 +1419,13 @@
                                 + AccessNetworkConstants.transportTypeToString(transport)
                                 + " data call list changed event. " + responseList);
                     } else {
+                        // If source PDN is reported lost, notify network agent that the PDN is
+                        // temporarily suspended and the old interface name is no longer usable.
+                        boolean currentPdnIsAlive = responseList.stream()
+                                .anyMatch(r -> mCid.get(mTransport) == r.getId());
+                        if (!currentPdnIsAlive) {
+                            notifyNetworkUnusable();
+                        }
                         log("Defer message " + eventToString(msg.what) + ":" + responseList);
                         deferMessage(msg);
                     }
@@ -1464,6 +1473,25 @@
             }
             return HANDLED;
         }
+
+        /**
+         * Notify network agent that the PDN is temporarily suspended and the old interface name is
+         * no longer usable. The state will be re-evaluated when the handover ends.
+         */
+        private void notifyNetworkUnusable() {
+            log(AccessNetworkConstants.transportTypeToString(mTransport)
+                    + " reports current PDN lost, update capability to SUSPENDED,"
+                    + " TNA interfaceName to \"\"");
+            mNetworkCapabilities = new NetworkCapabilities
+                    .Builder(mNetworkCapabilities)
+                    .removeCapability(
+                            NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+                    .build();
+            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+
+            mLinkProperties.setInterfaceName("");
+            mNetworkAgent.sendLinkProperties(mLinkProperties);
+        }
     }
 
     /**
@@ -2373,9 +2401,11 @@
         mFailCause = getFailCauseFromDataCallResponse(resultCode, response);
         validateDataCallResponse(response);
         if (mFailCause == DataFailCause.NONE) {
-            if (mDataNetworkController.isNetworkInterfaceExisting(response.getInterfaceName())) {
-                logl("Interface " + response.getInterfaceName() + " already existing. Silently "
-                        + "tear down now.");
+            DataNetwork dataNetwork = mDataNetworkController.getDataNetworkByInterface(
+                    response.getInterfaceName());
+            if (dataNetwork != null) {
+                logl("Interface " + response.getInterfaceName() + " has been already used by "
+                        + dataNetwork + ". Silently tear down now.");
                 // If this is a pre-5G data setup, that means APN database has some problems. For
                 // example, different APN settings have the same APN name.
                 if (response.getTrafficDescriptors().isEmpty()) {
@@ -2510,9 +2540,9 @@
             log("Remove network since deactivate request returned an error.");
             mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
             transitionTo(mDisconnectedState);
-        } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_2_0)) {
+        } else if (mPhone.getHalVersion(HAL_SERVICE_DATA).less(RIL.RADIO_HAL_VERSION_2_0)) {
             log("Remove network on deactivate data response on old HAL "
-                    + mPhone.getHalVersion());
+                    + mPhone.getHalVersion(HAL_SERVICE_DATA));
             mFailCause = DataFailCause.LOST_CONNECTION;
             transitionTo(mDisconnectedState);
         }
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 8f8da63..18e4769 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -968,7 +968,7 @@
         });
 
         mPhone.getServiceStateTracker().registerForServiceStateChanged(this,
-                EVENT_SERVICE_STATE_CHANGED);
+                EVENT_SERVICE_STATE_CHANGED, null);
         mDataConfigManager.registerCallback(new DataConfigManagerCallback(this::post) {
             @Override
             public void onCarrierConfigChanged() {
@@ -990,7 +990,7 @@
 
         if (!mAccessNetworksManager.isInLegacyMode()) {
             mPhone.getServiceStateTracker().registerForServiceStateChanged(this,
-                    EVENT_SERVICE_STATE_CHANGED);
+                    EVENT_SERVICE_STATE_CHANGED, null);
             mDataServiceManagers.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
                     .registerForServiceBindingChanged(this, EVENT_DATA_SERVICE_BINDING_CHANGED);
         }
@@ -1548,7 +1548,8 @@
 
             if (!mDataSettingsManager.isDataEnabled()
                     && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
-                    && mDataSettingsManager.isMmsAlwaysAllowed()) {
+                    && mDataSettingsManager.isMobileDataPolicyEnabled(TelephonyManager
+                    .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) {
                 // We reach here when data is disabled, but MMS always-allowed is enabled.
                 // (Note that isDataEnabled(ApnSetting.TYPE_MMS) returns true in this case, so it
                 // would not generate any soft disallowed reason. We need to explicitly handle it.)
@@ -2130,16 +2131,19 @@
     }
 
     /**
-     * Check if there are existing networks having the same interface name.
+     * Get data network by interface name.
      *
-     * @param interfaceName The interface name to check.
-     * @return {@code true} if the existing network has the same interface name.
+     * @param interfaceName The network interface name.
+     * @return The data network if found.
      */
-    public boolean isNetworkInterfaceExisting(@NonNull String interfaceName) {
+    @Nullable
+    public DataNetwork getDataNetworkByInterface(@NonNull String interfaceName) {
         return mDataNetworkList.stream()
                 .filter(dataNetwork -> !dataNetwork.isDisconnecting())
-                .anyMatch(dataNetwork -> interfaceName.equals(
-                        dataNetwork.getLinkProperties().getInterfaceName()));
+                .filter(dataNetwork -> interfaceName.equals(
+                        dataNetwork.getLinkProperties().getInterfaceName()))
+                .findFirst()
+                .orElse(null);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 7976362..7360cd0 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -954,6 +954,9 @@
                 ? apn1.getMtuV4() : apn2.getMtuV4());
         apnBuilder.setMtuV6(apn2.getMtuV6() <= ApnSetting.UNSET_MTU
                 ? apn1.getMtuV6() : apn2.getMtuV6());
+        // legacy properties that don't matter
+        apnBuilder.setMvnoType(apn1.getMvnoType());
+        apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData());
 
         // The following fields in apn1 and apn2 should be the same, otherwise ApnSetting.similar()
         // should fail earlier.
@@ -968,8 +971,6 @@
         apnBuilder.setMaxConns(apn1.getMaxConns());
         apnBuilder.setWaitTime(apn1.getWaitTime());
         apnBuilder.setMaxConnsTime(apn1.getMaxConnsTime());
-        apnBuilder.setMvnoType(apn1.getMvnoType());
-        apnBuilder.setMvnoMatchData(apn1.getMvnoMatchData());
         apnBuilder.setApnSetId(apn1.getApnSetId());
         apnBuilder.setCarrierId(apn1.getCarrierId());
         apnBuilder.setSkip464Xlat(apn1.getSkip464Xlat());
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index c593f88..15743d7 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -32,9 +32,11 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.MobileDataPolicy;
 import android.telephony.TelephonyRegistryManager;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.ApnType;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
@@ -42,13 +44,18 @@
 
 import com.android.internal.telephony.GlobalSettingsHelper;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SettingsObserver;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
+import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -59,6 +66,9 @@
  * data roaming settings, etc...
  */
 public class DataSettingsManager extends Handler {
+    /** Invalid mobile data policy **/
+    private static final int INVALID_MOBILE_DATA_POLICY = -1;
+
     /** Event for call state changed. */
     private static final int EVENT_CALL_STATE_CHANGED = 2;
     /** Event for subscriptions updated. */
@@ -67,10 +77,9 @@
     private static final int EVENT_SET_DATA_ENABLED_FOR_REASON = 5;
     /** Event for set data roaming enabled. */
     private static final int EVENT_SET_DATA_ROAMING_ENABLED = 6;
-    /** Event for set always allow MMS data. */
-    private static final int EVENT_SET_ALWAYS_ALLOW_MMS_DATA = 7;
-    /** Event for set allow data during voice call. */
-    private static final int EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL = 8;
+    /** Event for set mobile data policy. */
+    private static final int EVENT_SET_MOBILE_DATA_POLICY = 7;
+
     /** Event for device provisioned changed. */
     private static final int EVENT_PROVISIONED_CHANGED = 9;
     /** Event for provisioning data enabled setting changed. */
@@ -83,8 +92,8 @@
     private final SettingsObserver mSettingsObserver;
     private final String mLogTag;
     private final LocalLog mLocalLog = new LocalLog(128);
+    private Set<Integer> mEnabledMobileDataPolicy = new HashSet<>();
     private int mSubId;
-    private DataEnabledOverride mDataEnabledOverride;
 
     /** Data config manager */
     private final @NonNull DataConfigManager mDataConfigManager;
@@ -171,7 +180,7 @@
         mResolver = mPhone.getContext().getContentResolver();
         registerCallback(callback);
         mDataConfigManager = dataNetworkController.getDataConfigManager();
-        mDataEnabledOverride = getDataEnabledOverride();
+        refreshEnabledMobileDataPolicy();
         mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
         mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_POLICY, true);
         mDataEnabledSettings.put(TelephonyManager.DATA_ENABLED_REASON_CARRIER, true);
@@ -191,7 +200,7 @@
             }
             case EVENT_SUBSCRIPTIONS_CHANGED: {
                 mSubId = (int) msg.obj;
-                mDataEnabledOverride = getDataEnabledOverride();
+                refreshEnabledMobileDataPolicy();
                 updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER);
                 mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
                 break;
@@ -225,34 +234,10 @@
                 setDataRoamingFromUserAction();
                 break;
             }
-            case EVENT_SET_ALWAYS_ALLOW_MMS_DATA: {
-                boolean alwaysAllow = (boolean) msg.obj;
-                if (alwaysAllow == isMmsAlwaysAllowed()) {
-                    break;
-                }
-                logl("AlwaysAllowMmsData changed to " + alwaysAllow);
-                mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow);
-                if (SubscriptionController.getInstance()
-                        .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) {
-                    updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
-                    notifyDataEnabledOverrideChanged(alwaysAllow,
-                            TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED);
-                }
-                break;
-            }
-            case EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL: {
-                boolean allow = (boolean) msg.obj;
-                if (allow == isDataAllowedInVoiceCall()) {
-                    break;
-                }
-                logl("AllowDataDuringVoiceCall changed to " + allow);
-                mDataEnabledOverride.setDataAllowedInVoiceCall(allow);
-                if (SubscriptionController.getInstance()
-                        .setDataEnabledOverrideRules(mSubId, mDataEnabledOverride.getRules())) {
-                    updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
-                    notifyDataEnabledOverrideChanged(allow, TelephonyManager
-                            .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL);
-                }
+            case EVENT_SET_MOBILE_DATA_POLICY: {
+                int mobileDataPolicy = msg.arg1;
+                boolean enable = msg.arg2 == 1;
+                onSetMobileDataPolicy(mobileDataPolicy, enable);
                 break;
             }
             case EVENT_PROVISIONED_CHANGED:
@@ -399,9 +384,9 @@
             return isProvisioningDataEnabled();
         } else {
             boolean userDataEnabled = isUserDataEnabled();
-            // Check if we should temporarily enable data in certain conditions.
-            boolean isDataEnabledOverridden = mDataEnabledOverride
-                    .shouldOverrideDataEnabledSettings(mPhone, apnType);
+            // Check if we should temporarily enable data based on mobile data policy.
+            boolean isDataEnabledOverridden = isDataEnabledOverriddenForApn(apnType);
+
 
             return ((userDataEnabled || isDataEnabledOverridden)
                     && mDataEnabledSettings.get(TelephonyManager.DATA_ENABLED_REASON_POLICY)
@@ -593,44 +578,68 @@
         sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true).commit();
     }
 
-    private @NonNull DataEnabledOverride getDataEnabledOverride() {
-        return new DataEnabledOverride(SubscriptionController.getInstance()
-                .getDataEnabledOverrideRules(mSubId));
+    /** Refresh the enabled mobile data policies from Telephony database */
+    private void refreshEnabledMobileDataPolicy() {
+        mEnabledMobileDataPolicy = getMobileDataPolicyEnabled(SubscriptionController
+                .getInstance().getEnabledMobileDataPolicies(mSubId));
     }
 
     /**
-     * Set whether to always allow the MMS data connection.
-     * @param alwaysAllow {@code true} if MMS data is always allowed and {@code false} otherwise.
+     * @return {@code true} If the mobile data policy is enabled
      */
-    public void setAlwaysAllowMmsData(boolean alwaysAllow) {
-        obtainMessage(EVENT_SET_ALWAYS_ALLOW_MMS_DATA, alwaysAllow).sendToTarget();
+    public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int mobileDataPolicy) {
+        return mEnabledMobileDataPolicy.contains(mobileDataPolicy);
     }
 
     /**
-     * Check whether MMS is always allowed.
-     * @return {@code true} if MMS is always allowed and {@code false} otherwise.
+     * Set mobile data policy enabled status
+     * @param mobileDataPolicy The mobile data policy to set
+     * @param enable {@code true} to enable the policy; {@code false} to disable.
      */
-    public boolean isMmsAlwaysAllowed() {
-        return mDataEnabledOverride.isMmsAlwaysAllowed();
+    public void setMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
+        obtainMessage(EVENT_SET_MOBILE_DATA_POLICY, mobileDataPolicy, enable ? 1 : 0)
+                .sendToTarget();
     }
 
     /**
-     * Set whether to allow mobile data during voice call. This is used for allowing data on the
-     * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
-     * devices, users will not be able to use mobile data. By calling this API, data will be
-     * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
-     * @param allow {@code true} if data is allowed during a voice call and {@code false} otherwise.
+     * Store data mobile policy to Telephony database.
+     *
+     * @param mobileDataPolicy The mobile data policy that overrides user data enabled setting.
+     * @param enable {@code true} to enable the policy; {@code false} to remove the policy.
      */
-    public void setAllowDataDuringVoiceCall(boolean allow) {
-        obtainMessage(EVENT_SET_ALLOW_DATA_DURING_VOICE_CALL, allow).sendToTarget();
+    private void onSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy, boolean enable) {
+        if (enable == isMobileDataPolicyEnabled(mobileDataPolicy)) {
+            return;
+        }
+        metricsRecordSetMobileDataPolicy(mobileDataPolicy);
+
+        if (enable) {
+            mEnabledMobileDataPolicy.add(mobileDataPolicy);
+        } else {
+            mEnabledMobileDataPolicy.remove(mobileDataPolicy);
+        }
+
+        String enabledMobileDataPolicies = mEnabledMobileDataPolicy.stream().map(String::valueOf)
+                .collect(Collectors.joining(","));
+        if (SubscriptionController.getInstance().setEnabledMobileDataPolicies(
+                mSubId, enabledMobileDataPolicies)) {
+            logl(TelephonyUtils.mobileDataPolicyToString(mobileDataPolicy) + " changed to "
+                    + enable);
+            updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+            notifyDataEnabledOverrideChanged(enable, mobileDataPolicy);
+        } else {
+            loge("onSetMobileDataPolicy: failed to set " + enabledMobileDataPolicies);
+        }
     }
 
     /**
-     * Check whether data is allowed during a voice call.
-     * @return {@code true} if data is allowed during voice call and {@code false} otherwise.
+     * Record the number of times a mobile data policy is toggled to metrics.
+     * @param mobileDataPolicy The mobile data policy that's toggled
      */
-    public boolean isDataAllowedInVoiceCall() {
-        return mDataEnabledOverride.isDataAllowedInVoiceCall();
+    private void metricsRecordSetMobileDataPolicy(@MobileDataPolicy int mobileDataPolicy) {
+        if (mobileDataPolicy == TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) {
+            DeviceTelephonyPropertiesStats.recordAutoDataSwitchFeatureToggle();
+        }
     }
 
     /**
@@ -659,6 +668,84 @@
     }
 
     /**
+     * Return the parsed mobile data policies.
+     *
+     * @param policies New mobile data policies in String format.
+     * @return A Set of parsed mobile data policies.
+     */
+    public @NonNull @MobileDataPolicy Set<Integer> getMobileDataPolicyEnabled(
+            @NonNull String policies) {
+        Set<Integer> mobileDataPolicies = new HashSet<>();
+        String[] rulesString = policies.trim().split("\\s*,\\s*");
+        for (String rule : rulesString) {
+            if (!TextUtils.isEmpty(rule)) {
+                int parsedDataPolicy = parsePolicyFrom(rule);
+                if (parsedDataPolicy != INVALID_MOBILE_DATA_POLICY) {
+                    mobileDataPolicies.add(parsedDataPolicy);
+                }
+            }
+        }
+        return mobileDataPolicies;
+    }
+
+    /**
+     * Parse a mobile data policy retrieved from Telephony db.
+     * If the policy is in legacy format, convert it into the corresponding mobile data policy.
+     *
+     * @param policy Mobile data policy to be parsed from.
+     * @return Parsed mobile data policy. {@link #INVALID_MOBILE_DATA_POLICY} if string can't be
+     * parsed into a mobile data policy.
+     */
+    private @MobileDataPolicy int parsePolicyFrom(@NonNull String policy) {
+        int dataPolicy;
+        try {
+            // parse as new override policy
+            dataPolicy = Integer.parseInt(policy);
+        } catch (NumberFormatException e) {
+            dataPolicy = INVALID_MOBILE_DATA_POLICY;
+            loge("parsePolicyFrom: invalid mobile data policy format: "  + policy);
+        }
+        return dataPolicy;
+    }
+
+    /**
+     * Check if data enabled is temporarily overridden in certain conditions.
+     *
+     * @param apnType The APN type to check.
+     * @return {@code true} if data enabled should be overridden.
+     */
+    private boolean isDataEnabledOverriddenForApn(@ApnType int apnType) {
+        boolean overridden = false;
+
+        // mobile data policy : MMS always allowed
+        if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED)) {
+            overridden = apnType == ApnSetting.TYPE_MMS;
+        }
+
+        SubscriptionController subscriptionController = SubscriptionController.getInstance();
+        boolean isNonDds = mPhone.getSubId() != subscriptionController.getDefaultDataSubId();
+
+        // mobile data policy : data during call
+        if (isMobileDataPolicyEnabled(TelephonyManager
+                .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL)) {
+            overridden = isNonDds && mPhone.getState() != PhoneConstants.State.IDLE;
+        }
+
+        // mobile data policy : auto data switch
+        if (isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) {
+            // check user enabled data on the default data phone
+            Phone defaultDataPhone = PhoneFactory.getPhone(subscriptionController.getPhoneId(
+                    subscriptionController.getDefaultDataSubId()));
+            if (defaultDataPhone == null) {
+                loge("isDataEnabledOverriddenForApn: unexpected defaultDataPhone is null");
+            } else {
+                overridden = isNonDds && defaultDataPhone.isUserDataEnabled();
+            }
+        }
+        return overridden;
+    }
+
+    /**
      * Register the callback for receiving information from {@link DataSettingsManager}.
      *
      * @param callback The callback.
@@ -700,7 +787,8 @@
                 + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
                 + ", mIsDataEnabled=" + mIsDataEnabled
                 + ", mDataEnabledSettings=" + mDataEnabledSettings
-                + ", mDataEnabledOverride=" + mDataEnabledOverride
+                + ", mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
+                .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(","))
                 + "]";
     }
 
@@ -756,7 +844,8 @@
                 .map(entry ->
                         dataEnabledChangedReasonToString(entry.getKey()) + "=" + entry.getValue())
                 .collect(Collectors.joining(", ")));
-        pw.println("mDataEnabledOverride=" + mDataEnabledOverride);
+        pw.println("mEnabledMobileDataPolicy=" + mEnabledMobileDataPolicy.stream()
+                .map(TelephonyUtils::mobileDataPolicyToString).collect(Collectors.joining(",")));
         pw.println("Local logs:");
         pw.increaseIndent();
         mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index e467815..d012033 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -151,6 +151,8 @@
     private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs;
     /** Whether current network is good or not */
     private boolean mIsValidNetwork;
+    /** Whether data stall recovery is triggered or not */
+    private boolean mRecoveryTriggered = false;
     /** Whether data stall happened or not. */
     private boolean mDataStalled;
     /** Whether the result of last action(RADIO_RESTART) reported. */
@@ -352,6 +354,7 @@
      */
     private void reset() {
         mIsValidNetwork = true;
+        mRecoveryTriggered = false;
         mIsAttemptedAllSteps = false;
         mRadioStateChangedDuringDataStall = false;
         mIsAirPlaneModeEnableDuringDataStall = false;
@@ -373,15 +376,12 @@
         setNetworkValidationState(isValid);
         if (isValid) {
             reset();
-        } else {
-            if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
-                mIsValidNetwork = false;
-                if (isRecoveryNeeded(true)) {
-                    log("trigger data stall recovery");
-                    mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
-                    sendMessage(obtainMessage(EVENT_DO_RECOVERY));
-                }
-            }
+        } else if (isRecoveryNeeded(true)) {
+            // Set the network as invalid, because recovery is needed
+            mIsValidNetwork = false;
+            log("trigger data stall recovery");
+            mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
+            sendMessage(obtainMessage(EVENT_DO_RECOVERY));
         }
     }
 
@@ -455,7 +455,7 @@
      * @return {@code true} if recovery already started, {@code false} recovery not started.
      */
     private boolean isRecoveryAlreadyStarted() {
-        return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST;
+        return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST || mRecoveryTriggered;
     }
 
     /**
@@ -542,6 +542,12 @@
     private boolean isRecoveryNeeded(boolean isNeedToCheckTimer) {
         logv("enter: isRecoveryNeeded()");
 
+        // Skip if network is invalid and recovery was not started yet
+        if (!mIsValidNetwork && !isRecoveryAlreadyStarted()) {
+            logl("skip when network still remains invalid and recovery was not started yet");
+            return false;
+        }
+
         // Skip recovery if we have already attempted all steps.
         if (mIsAttemptedAllSteps) {
             logl("skip retrying continue recovery action");
@@ -577,7 +583,6 @@
             logl("skip data stall recovery as data not connected");
             return false;
         }
-
         return true;
     }
 
@@ -652,6 +657,7 @@
     private void doRecovery() {
         @RecoveryAction final int recoveryAction = getRecoveryAction();
         final int signalStrength = mPhone.getSignalStrength().getLevel();
+        mRecoveryTriggered = true;
 
         // DSRM used sendMessageDelayed to process the next event EVENT_DO_RECOVERY, so it need
         // to check the condition if DSRM need to process the recovery action.
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 897f412..fc0c4f1 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -41,6 +41,7 @@
 import android.util.ArrayMap;
 
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
+import com.android.telephony.Rlog;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -61,6 +62,7 @@
     /** The time format for converting time to readable string. */
     private static final SimpleDateFormat TIME_FORMAT =
             new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);
+    private static final String TAG = "DataUtils";
 
     /**
      * Get the network capability from the string.
@@ -164,6 +166,7 @@
             case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH:
                 return "PRIORITIZE_BANDWIDTH";
             default:
+                loge("Unknown network capability(" + netCap + ")");
                 return "Unknown(" + netCap + ")";
         }
     }
@@ -209,7 +212,9 @@
         switch (status) {
             case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID";
             case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID";
-            default: return "UNKNOWN(" + status + ")";
+            default:
+                loge("Unknown validation status(" + status + ")");
+                return "UNKNOWN(" + status + ")";
         }
     }
 
@@ -366,6 +371,7 @@
             case ImsFeature.FEATURE_MMTEL: return "MMTEL";
             case ImsFeature.FEATURE_RCS: return "RCS";
             default:
+                loge("Unknown IMS feature(" + imsFeature + ")");
                 return "Unknown(" + imsFeature + ")";
         }
     }
@@ -468,7 +474,9 @@
             case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE";
             case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE";
             case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT";
-            default: return "UNKNOWN(" + linkStatus + ")";
+            default:
+                loge("Unknown link status(" + linkStatus + ")");
+                return "UNKNOWN(" + linkStatus + ")";
         }
     }
 
@@ -505,7 +513,13 @@
             case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT";
             case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT";
             case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT";
-            default: return "UNKNOWN(" + dataActivity + ")";
+            default:
+                loge("Unknown data activity(" + dataActivity + ")");
+                return "UNKNOWN(" + dataActivity + ")";
         }
     }
+
+    private static void loge(String msg) {
+        Rlog.e(TAG, msg);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
index efed84a..939e971 100644
--- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
+++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -32,8 +32,6 @@
 import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.OutcomeReceiver;
-import android.os.Registrant;
-import android.os.RegistrantList;
 import android.preference.PreferenceManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.DataActivityType;
@@ -191,7 +189,6 @@
     private String mBandwidthUpdatePlmn = UNKNOWN_PLMN;
     private BandwidthState mTxState = new BandwidthState(LINK_TX);
     private BandwidthState mRxState = new BandwidthState(LINK_RX);
-    private RegistrantList mBandwidthChangedRegistrants = new RegistrantList();
     private long mLastPlmnOrRatChangeTimeMs;
     private long mLastDrsOrRatChangeTimeMs;
 
@@ -351,33 +348,6 @@
     }
 
     /**
-     * Registers for bandwidth estimation change. The bandwidth will be returned
-     *      * {@link AsyncResult#result} as a {@link Pair} Object.
-     *      * The {@link AsyncResult} will be in the notification {@link Message#obj}.
-     * @param h handler to notify
-     * @param what what code of message when delivered
-     * @param obj placed in Message.obj
-     *
-     * @deprecated Use {@link #registerCallback(LinkBandwidthEstimatorCallback)}.
-     */
-    @Deprecated //TODO: Remove once old data stack is removed.
-    public void registerForBandwidthChanged(Handler h, int what, Object obj) {
-        Registrant r = new Registrant(h, what, obj);
-        mBandwidthChangedRegistrants.add(r);
-    }
-
-    /**
-     * Unregisters for bandwidth estimation change.
-     * @param h handler to notify
-     *
-     * @deprecated Use {@link #unregisterCallback(LinkBandwidthEstimatorCallback)}.
-     */
-    @Deprecated //TODO: Remove once old data stack is removed.
-    public void unregisterForBandwidthChanged(Handler h) {
-        mBandwidthChangedRegistrants.remove(h);
-    }
-
-    /**
      * Register the callback for receiving information from {@link LinkBandwidthEstimator}.
      *
      * @param callback The callback.
@@ -930,9 +900,6 @@
 
     private void sendLinkBandwidthToDataConnection(int linkBandwidthTxKps, int linkBandwidthRxKps) {
         logv("send to DC tx " + linkBandwidthTxKps + " rx " + linkBandwidthRxKps);
-        Pair<Integer, Integer> bandwidthInfo =
-                new Pair<Integer, Integer>(linkBandwidthTxKps, linkBandwidthRxKps);
-        mBandwidthChangedRegistrants.notifyRegistrants(new AsyncResult(null, bandwidthInfo, null));
         mLinkBandwidthEstimatorCallbacks.forEach(callback -> callback.invokeFromExecutor(
                 () -> callback.onBandwidthChanged(linkBandwidthTxKps, linkBandwidthRxKps)));
     }
diff --git a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java b/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java
deleted file mode 100644
index 554177a..0000000
--- a/src/java/com/android/internal/telephony/data/NotifyQosSessionInterface.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import android.annotation.NonNull;
-import android.net.NetworkAgent;
-import android.net.QosSessionAttributes;
-
-/**
- * The temporary interface that is shared by
- * {@link com.android.internal.telephony.dataconnection.DcNetworkAgent} and
- * {@link com.android.internal.telephony.data.TelephonyNetworkAgent} so they can both interact
- * with {@link QosCallbackTracker}.
- */
-// TODO: Remove after DcNetworkAgent is removed.
-public interface NotifyQosSessionInterface {
-    /**
-     * Sends the attributes of Qos Session back to the Application. This method is create for
-     * Mockito to mock since
-     * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is
-     * {@code final} that can't be mocked.
-     *
-     * @param qosCallbackId the callback id that the session belongs to.
-     * @param sessionId the unique session id across all Qos Sessions.
-     * @param attributes the attributes of the Qos Session.
-     */
-    void notifyQosSessionAvailable(int qosCallbackId, int sessionId,
-            @NonNull QosSessionAttributes attributes);
-
-    /**
-     * Sends event that the Qos Session was lost. This method is create for Mockito to mock
-     * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be
-     * mocked..
-     *
-     * @param qosCallbackId the callback id that the session belongs to.
-     * @param sessionId the unique session id across all Qos Sessions.
-     * @param qosSessionType the session type {@code QosSession#QosSessionType}.
-     */
-    void notifyQosSessionLost(int qosCallbackId, int sessionId, int qosSessionType);
-}
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 5bd4de9..f3f72f1 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -31,6 +31,10 @@
 import static java.util.Arrays.copyOf;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -46,6 +50,7 @@
 import android.net.TelephonyNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -53,14 +58,16 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.RemoteException;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyRegistryManager;
-import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
@@ -83,12 +90,11 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
-import com.android.internal.telephony.dataconnection.ApnConfigTypeRepository;
-import com.android.internal.telephony.dataconnection.DcRequest;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch;
+import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
 
@@ -96,7 +102,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -114,7 +119,21 @@
     private static final String LOG_TAG = "PhoneSwitcher";
     protected static final boolean VDBG = false;
 
-    private static final int DEFAULT_NETWORK_CHANGE_TIMEOUT_MS = 5000;
+    /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */
+    private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+    /**
+     * When starting this activity, this extra can also be specified to supply a Bundle of arguments
+     * to pass to that fragment when it is instantiated during the initial creation of the activity.
+     */
+    private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
+            ":settings:show_fragment_args";
+    /** The res Id of the auto data switch fragment in settings. **/
+    private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
+    /** Notification tag **/
+    private static final String AUTO_DATA_SWITCH_NOTIFICATION_TAG = "auto_data_switch";
+    /** Notification ID **/
+    private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1;
+
     private static final int MODEM_COMMAND_RETRY_PERIOD_MS     = 5000;
     // After the emergency call ends, wait for a few seconds to see if we enter ECBM before starting
     // the countdown to remove the emergency DDS override.
@@ -182,7 +201,6 @@
         }
     }
 
-    protected final List<DcRequest> mPrioritizedDcRequests = new ArrayList<>();
     private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList();
     protected final RegistrantList mActivePhoneRegistrants;
     protected final SubscriptionController mSubscriptionController;
@@ -194,6 +212,10 @@
     @VisibleForTesting
     protected final CellularNetworkValidator mValidator;
     private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+    /** The last reason for auto switch (e.g. CBRS) **/
+    private int mLastAutoSelectedSwitchReason = -1;
+    /** {@code true} if we've displayed the notification the first time auto switch occurs **/
+    private boolean mDisplayedAutoSwitchNotification = false;
     private boolean mPendingSwitchNeedValidation;
     @VisibleForTesting
     public final CellularNetworkValidator.ValidationCallback mValidationCallback =
@@ -226,9 +248,15 @@
     // Internet data if mOpptDataSubId is not set.
     protected int mPrimaryDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-    // mOpptDataSubId must be an active subscription. If it's set, it overrides mPrimaryDataSubId
-    // to be used for Internet data.
-    private int mOpptDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+    // The automatically suggested preferred data subId (by e.g. CBRS or auto data switch), a
+    // candidate for preferred data subId, which is eventually presided by
+    // updatePreferredDataPhoneId().
+    // If CBRS/auto switch feature selects the primary data subId as the preferred data subId,
+    // its value will be DEFAULT_SUBSCRIPTION_ID.
+    private int mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+
+    /** The count of consecutive auto switch validation failure **/
+    private int mAutoSwitchRetryFailedCount = 0;
 
     // The phone ID that has an active voice call. If set, and its mobile data setting is on,
     // it will become the mPreferredDataPhoneId.
@@ -260,6 +288,16 @@
 
     private ISetOpportunisticDataCallback mSetOpptSubCallback;
 
+    /** Data config manager callback for updating device config. **/
+    private final DataConfigManager.DataConfigManagerCallback mDataConfigManagerCallback =
+            new DataConfigManager.DataConfigManagerCallback(this::post) {
+        @Override
+        public void onDeviceConfigChanged() {
+            log("onDeviceConfigChanged");
+            PhoneSwitcher.this.updateConfig();
+        }
+    };
+
     private static final int EVENT_PRIMARY_DATA_SUB_CHANGED       = 101;
     protected static final int EVENT_SUBSCRIPTION_CHANGED           = 102;
     private static final int EVENT_REQUEST_NETWORK                = 103;
@@ -276,14 +314,12 @@
     // mEmergencyOverride, start the countdown to remove the override using the message
     // EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE. The only exception to this is if the device moves to
     // ECBM, which is detected by EVENT_EMERGENCY_TOGGLE.
-    @VisibleForTesting
-    public static final int EVENT_PRECISE_CALL_STATE_CHANGED      = 109;
+    private static final int EVENT_PRECISE_CALL_STATE_CHANGED     = 109;
     private static final int EVENT_NETWORK_VALIDATION_DONE        = 110;
-    private static final int EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK = 111;
+    private static final int EVENT_EVALUATE_AUTO_SWITCH           = 111;
     private static final int EVENT_MODEM_COMMAND_DONE             = 112;
     private static final int EVENT_MODEM_COMMAND_RETRY            = 113;
-    @VisibleForTesting
-    public static final int EVENT_DATA_ENABLED_CHANGED            = 114;
+    private static final int EVENT_SERVICE_STATE_CHANGED          = 114;
     // An emergency call is about to be originated and requires the DDS to be overridden.
     // Uses EVENT_PRECISE_CALL_STATE_CHANGED message to start countdown to finish override defined
     // in mEmergencyOverride. If EVENT_PRECISE_CALL_STATE_CHANGED does not come in
@@ -292,12 +328,11 @@
     // If it exists, remove the current mEmergencyOverride DDS override.
     private static final int EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE  = 116;
     // If it exists, remove the current mEmergencyOverride DDS override.
-    @VisibleForTesting
-    public static final int EVENT_MULTI_SIM_CONFIG_CHANGED        = 117;
+    private static final int EVENT_MULTI_SIM_CONFIG_CHANGED       = 117;
     private static final int EVENT_NETWORK_AVAILABLE              = 118;
     private static final int EVENT_PROCESS_SIM_STATE_CHANGE       = 119;
-    @VisibleForTesting
-    public static final int EVENT_IMS_RADIO_TECH_CHANGED          = 120;
+    private static final int EVENT_IMS_RADIO_TECH_CHANGED         = 120;
+    private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE   = 121;
 
     // List of events triggers re-evaluations
     private static final String EVALUATION_REASON_RADIO_ON = "EVENT_RADIO_ON";
@@ -317,13 +352,24 @@
     // Default timeout value of network validation in millisecond.
     private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
 
-    private Boolean mHasRegisteredDefaultNetworkChangeCallback = false;
-
     private ConnectivityManager mConnectivityManager;
     private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
 
     private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
 
+    /**
+     * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
+     * in service, wifi is the default active network.etc), while -1 indicates auto switch
+     * feature disabled.
+     */
+    private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
+
+    /**
+     * The maximum number of retries when a validation for switching failed.
+     */
+    private int mAutoDataSwitchValidationMaxRetry =
+            DataConfigManager.DEFAULT_AUTO_DATA_SWITCH_MAX_RETRY;
+
     /** Data settings manager callback. Key is the phone id. */
     private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
             new ArrayMap<>();
@@ -331,18 +377,37 @@
     private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
         public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
+        public boolean isDefaultNetworkOnCellular = false;
         @Override
         public void onCapabilitiesChanged(Network network,
                 NetworkCapabilities networkCapabilities) {
-            if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
-                    && SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
-                    && mExpectedSubId == getSubIdFromNetworkSpecifier(
-                            networkCapabilities.getNetworkSpecifier())) {
-                logDataSwitchEvent(
-                        mExpectedSubId,
-                        TelephonyEvent.EventState.EVENT_STATE_END,
-                        mSwitchReason);
-                removeDefaultNetworkChangeCallback();
+            if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+                isDefaultNetworkOnCellular = true;
+                if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
+                        && mExpectedSubId == getSubIdFromNetworkSpecifier(
+                        networkCapabilities.getNetworkSpecifier())) {
+                    logDataSwitchEvent(
+                            mExpectedSubId,
+                            TelephonyEvent.EventState.EVENT_STATE_END,
+                            mSwitchReason);
+                    mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+                    mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
+                }
+            } else {
+                if (isDefaultNetworkOnCellular) {
+                    // non-cellular transport is active
+                    isDefaultNetworkOnCellular = false;
+                    log("default network is active on non cellular");
+                    evaluateIfAutoSwitchIsNeeded();
+                }
+            }
+        }
+
+        @Override
+        public void onLost(Network network) {
+            // try find an active sub to switch to
+            if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+                sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
             }
         }
     }
@@ -417,7 +482,7 @@
         boolean isOnOriginalNetwork = (imsRegTech != REGISTRATION_TECH_IWLAN)
                 && (imsRegTech != REGISTRATION_TECH_CROSS_SIM);
         if (!isOnOriginalNetwork) {
-            log("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch");
+            logl("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch");
         }
         return isOnOriginalNetwork;
     }
@@ -437,7 +502,7 @@
         }
 
         if (mPhoneIdInVoiceCall != oldPhoneIdInVoiceCall) {
-            log("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall
+            logl("isPhoneInVoiceCallChanged from phoneId " + oldPhoneIdInVoiceCall
                     + " to phoneId " + mPhoneIdInVoiceCall);
             return true;
         } else {
@@ -464,13 +529,13 @@
         }
     }
 
-    private void evaluateIfDataSwitchIsNeeded(String reason) {
-        if (onEvaluate(REQUESTS_UNCHANGED, reason)) {
+    private void evaluateIfImmediateDataSwitchIsNeeded(String evaluationReason, int switchReason) {
+        if (onEvaluate(REQUESTS_UNCHANGED, evaluationReason)) {
             logDataSwitchEvent(mPreferredDataSubId.get(),
                     TelephonyEvent.EventState.EVENT_STATE_START,
-                    DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+                    switchReason);
             registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
-                    DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+                    switchReason);
         }
     }
 
@@ -494,9 +559,9 @@
         mContext.registerReceiver(mSimStateIntentReceiver, filter);
 
         mActivePhoneRegistrants = new RegistrantList();
-        for (int i = 0; i < mActiveModemCount; i++) {
-            mPhoneStates[i] = new PhoneState();
-            Phone phone = PhoneFactory.getPhone(i);
+        for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
+            mPhoneStates[phoneId] = new PhoneState();
+            Phone phone = PhoneFactory.getPhone(phoneId);
             if (phone != null) {
                 phone.registerForEmergencyCallToggle(
                         this, EVENT_EMERGENCY_TOGGLE, null);
@@ -507,23 +572,22 @@
                     phone.getImsPhone().registerForPreciseCallStateChanged(
                             this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
                 }
-                if (phone.isUsingNewDataStack()) {
-                    mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
-                            v -> new DataSettingsManagerCallback(this::post) {
-                                @Override
-                                public void onDataEnabledChanged(boolean enabled,
-                                        @TelephonyManager.DataEnabledChangedReason int reason,
-                                        @NonNull String callingPackage) {
-                                    evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
-                                }});
-                    phone.getDataSettingsManager().registerCallback(
-                            mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
-                } else {
-                    phone.getDataEnabledSettings().registerForDataEnabledChanged(
-                            this, EVENT_DATA_ENABLED_CHANGED, null);
-                }
-
-                registerForImsRadioTechChange(context, i);
+                mDataSettingsManagerCallbacks.computeIfAbsent(phoneId,
+                        v -> new DataSettingsManagerCallback(this::post) {
+                            @Override
+                            public void onDataEnabledChanged(boolean enabled,
+                                    @TelephonyManager.DataEnabledChangedReason int reason,
+                                    @NonNull String callingPackage) {
+                                logl("user changed data settings");
+                                evaluateIfImmediateDataSwitchIsNeeded("user changed data settings",
+                                        DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+                                evaluateIfAutoSwitchIsNeeded();
+                            }});
+                phone.getDataSettingsManager().registerCallback(
+                        mDataSettingsManagerCallbacks.get(phoneId));
+                phone.getServiceStateTracker().registerForServiceStateChanged(this,
+                        EVENT_SERVICE_STATE_CHANGED, phoneId);
+                registerForImsRadioTechChange(context, phoneId);
             }
             Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
@@ -547,6 +611,8 @@
         PhoneConfigurationManager.registerForMultiSimConfigChange(
                 this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
 
+        mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
@@ -580,7 +646,7 @@
 
         updateHalCommandToUse();
 
-        log("PhoneSwitcher started");
+        logl("PhoneSwitcher started");
     }
 
     private final BroadcastReceiver mDefaultDataChangedReceiver = new BroadcastReceiver() {
@@ -600,7 +666,7 @@
                         TelephonyManager.SIM_STATE_UNKNOWN);
                 int slotIndex = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
                         SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-                log("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state);
+                logl("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state);
                 obtainMessage(EVENT_PROCESS_SIM_STATE_CHANGE, slotIndex, state).sendToTarget();
             }
         }
@@ -618,7 +684,7 @@
 
         IccCard iccCard = PhoneFactory.getPhone(slotIndex).getIccCard();
         if (!iccCard.isEmptyProfile() && uiccAppsEnabled) {
-            log("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex);
+            logl("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex);
             return true;
         } else {
             return false;
@@ -641,14 +707,22 @@
                 onEvaluate(REQUESTS_UNCHANGED, "subChanged");
                 break;
             }
+            case EVENT_SERVICE_STATE_CHANGED: {
+                AsyncResult ar = (AsyncResult) msg.obj;
+                final int phoneId = (int) ar.userObj;
+                onServiceStateChanged(phoneId);
+                break;
+            }
+            case EVENT_MEETS_AUTO_DATA_SWITCH_STATE: {
+                final int targetSubId = msg.arg1;
+                final boolean needValidation = (boolean) msg.obj;
+                validate(targetSubId, needValidation,
+                        DataSwitch.Reason.DATA_SWITCH_REASON_AUTO, null);
+                break;
+            }
             case EVENT_PRIMARY_DATA_SUB_CHANGED: {
-                if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) {
-                    logDataSwitchEvent(mPreferredDataSubId.get(),
-                            TelephonyEvent.EventState.EVENT_STATE_START,
-                            DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
-                    registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
-                            DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
-                }
+                evaluateIfImmediateDataSwitchIsNeeded("primary data sub changed",
+                        DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
                 break;
             }
             case EVENT_REQUEST_NETWORK: {
@@ -662,7 +736,7 @@
             case EVENT_EMERGENCY_TOGGLE: {
                 boolean isInEcm = isInEmergencyCallbackMode();
                 if (mEmergencyOverride != null) {
-                    log("Emergency override - ecbm status = " + isInEcm);
+                    logl("Emergency override - ecbm status = " + isInEcm);
                     if (isInEcm) {
                         // The device has gone into ECBM. Wait until it's out.
                         removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
@@ -676,6 +750,9 @@
                 onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
                 break;
             }
+            case EVENT_EVALUATE_AUTO_SWITCH:
+                evaluateIfAutoSwitchIsNeeded();
+                break;
             case EVENT_RADIO_CAPABILITY_CHANGED: {
                 final int phoneId = msg.arg1;
                 sendRilCommands(phoneId);
@@ -702,7 +779,8 @@
                 if (!isPhoneInVoiceCallChanged()) {
                     break;
                 }
-                evaluateIfDataSwitchIsNeeded("EVENT_IMS_RADIO_TECH_CHANGED");
+                evaluateIfImmediateDataSwitchIsNeeded("Ims radio tech changed",
+                        DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
                 break;
 
             case EVENT_PRECISE_CALL_STATE_CHANGED: {
@@ -740,13 +818,10 @@
                         mEmergencyOverride.mPendingOriginatingCall = false;
                     }
                 }
-                evaluateIfDataSwitchIsNeeded("EVENT_PRECISE_CALL_STATE_CHANGED");
+                evaluateIfImmediateDataSwitchIsNeeded("precise call state changed",
+                        DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
                 break;
             }
-
-            case EVENT_DATA_ENABLED_CHANGED:
-                evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
-                break;
             case EVENT_NETWORK_VALIDATION_DONE: {
                 int subId = msg.arg1;
                 boolean passed = (msg.arg2 == 1);
@@ -759,10 +834,6 @@
                 onNetworkAvailable(subId, network);
                 break;
             }
-            case EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK: {
-                removeDefaultNetworkChangeCallback();
-                break;
-            }
             case EVENT_MODEM_COMMAND_DONE: {
                 AsyncResult ar = (AsyncResult) msg.obj;
                 onDdsSwitchResponse(ar);
@@ -771,10 +842,10 @@
             case EVENT_MODEM_COMMAND_RETRY: {
                 int phoneId = (int) msg.obj;
                 if (isPhoneIdValidForRetry(phoneId)) {
-                    log("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId);
+                    logl("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId);
                     sendRilCommands(phoneId);
                 } else {
-                    log("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed");
+                    logl("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed");
                     mCurrentDdsSwitchFailure.get(phoneId).clear();
                 }
                 break;
@@ -786,7 +857,7 @@
                     // being overridden, ignore. We should not try to switch DDS while already
                     // waiting for SUPL.
                     if (mEmergencyOverride.mPhoneId != req.mPhoneId) {
-                        log("emergency override requested for phone id " + req.mPhoneId + " when "
+                        logl("emergency override requested for phone id " + req.mPhoneId + " when "
                                 + "there is already an override in place for phone id "
                                 + mEmergencyOverride.mPhoneId + ". Ignoring.");
                         if (req.isCallbackAvailable()) {
@@ -806,7 +877,7 @@
                     mEmergencyOverride = req;
                 }
 
-                log("new emergency override - " + mEmergencyOverride);
+                logl("new emergency override - " + mEmergencyOverride);
                 // a new request has been created, remove any previous override complete scheduled.
                 removeMessages(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
                 Message msg2 = obtainMessage(EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE);
@@ -824,7 +895,7 @@
                 break;
             }
             case EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE: {
-                log("Emergency override removed - " + mEmergencyOverride);
+                logl("Emergency override removed - " + mEmergencyOverride);
                 mEmergencyOverride = null;
                 onEvaluate(REQUESTS_UNCHANGED, "emer_rm_override_dds");
                 break;
@@ -839,7 +910,7 @@
                 int simState = (int) msg.arg2;
 
                 if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
-                    log("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: "
+                    logl("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: "
                             + slotIndex);
                 } else if (mCurrentDdsSwitchFailure.get(slotIndex).contains(
                         CommandException.Error.INVALID_SIM_STATE)
@@ -847,11 +918,40 @@
                         && isSimApplicationReady(slotIndex)) {
                     sendRilCommands(slotIndex);
                 }
+
+                registerConfigChange();
                 break;
             }
         }
     }
 
+    /**
+     * Register for device config change on the primary data phone.
+     */
+    private void registerConfigChange() {
+        Phone phone = findPhoneById(mSubscriptionController.getPhoneId(mPrimaryDataSubId));
+        if (phone != null) {
+            DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
+            dataConfig.registerCallback(mDataConfigManagerCallback);
+            updateConfig();
+            sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+        }
+    }
+
+    /**
+     * Update data config.
+     */
+    private void updateConfig() {
+        Phone phone = findPhoneById(mSubscriptionController.getPhoneId(mPrimaryDataSubId));
+        if (phone != null) {
+            DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
+            mAutoDataSwitchAvailabilityStabilityTimeThreshold =
+                    dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+            mAutoDataSwitchValidationMaxRetry =
+                    dataConfig.getAutoDataSwitchValidationMaxRetry();
+        }
+    }
+
     private synchronized void onMultiSimConfigChanged(int activeModemCount) {
         // No change.
         if (mActiveModemCount == activeModemCount) return;
@@ -880,22 +980,22 @@
                         this, EVENT_PRECISE_CALL_STATE_CHANGED, null);
             }
 
-            if (phone.isUsingNewDataStack()) {
-                mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
-                        v -> new DataSettingsManagerCallback(this::post) {
-                            @Override
-                            public void onDataEnabledChanged(boolean enabled,
-                                    @TelephonyManager.DataEnabledChangedReason int reason,
-                                    @NonNull String callingPackage) {
-                                evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
-                            }
-                        });
-                phone.getDataSettingsManager().registerCallback(
-                        mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
-            } else {
-                phone.getDataEnabledSettings().registerForDataEnabledChanged(
-                        this, EVENT_DATA_ENABLED_CHANGED, null);
-            }
+            mDataSettingsManagerCallbacks.computeIfAbsent(phone.getPhoneId(),
+                    v -> new DataSettingsManagerCallback(this::post) {
+                        @Override
+                        public void onDataEnabledChanged(boolean enabled,
+                                @TelephonyManager.DataEnabledChangedReason int reason,
+                                @NonNull String callingPackage) {
+                            logl("user changed data settings");
+                            evaluateIfImmediateDataSwitchIsNeeded("user changed data settings",
+                                    DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+                            evaluateIfAutoSwitchIsNeeded();
+                        }
+                    });
+            phone.getDataSettingsManager().registerCallback(
+                    mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
+            phone.getServiceStateTracker().registerForServiceStateChanged(this,
+                    EVENT_SERVICE_STATE_CHANGED, phoneId);
 
             Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
@@ -941,79 +1041,26 @@
     }
 
     private void onRequestNetwork(NetworkRequest networkRequest) {
-        if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
-            TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
-                    networkRequest, PhoneFactory.getDefaultPhone());
-            if (!mNetworkRequestList.contains(telephonyNetworkRequest)) {
-                mNetworkRequestList.add(telephonyNetworkRequest);
-                onEvaluate(REQUESTS_CHANGED, "netRequest");
-            }
-            return;
-        }
-        final DcRequest dcRequest =
-                DcRequest.create(networkRequest, createApnRepository(networkRequest));
-        if (dcRequest != null) {
-            if (!mPrioritizedDcRequests.contains(dcRequest)) {
-                collectRequestNetworkMetrics(networkRequest);
-                mPrioritizedDcRequests.add(dcRequest);
-                Collections.sort(mPrioritizedDcRequests);
-                onEvaluate(REQUESTS_CHANGED, "netRequest");
-                if (VDBG) log("Added DcRequest, size: " + mPrioritizedDcRequests.size());
-            }
+        TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
+                networkRequest, PhoneFactory.getDefaultPhone());
+        if (!mNetworkRequestList.contains(telephonyNetworkRequest)) {
+            mNetworkRequestList.add(telephonyNetworkRequest);
+            onEvaluate(REQUESTS_CHANGED, "netRequest");
         }
     }
 
     private void onReleaseNetwork(NetworkRequest networkRequest) {
-        if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
-            TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
-                    networkRequest, PhoneFactory.getDefaultPhone());
-            if (mNetworkRequestList.remove(telephonyNetworkRequest)) {
-                onEvaluate(REQUESTS_CHANGED, "netReleased");
-                collectReleaseNetworkMetrics(networkRequest);
-            }
-            return;
+        TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
+                networkRequest, PhoneFactory.getDefaultPhone());
+        if (mNetworkRequestList.remove(telephonyNetworkRequest)) {
+            onEvaluate(REQUESTS_CHANGED, "netReleased");
+            collectReleaseNetworkMetrics(networkRequest);
         }
-        final DcRequest dcRequest =
-                DcRequest.create(networkRequest, createApnRepository(networkRequest));
-        if (dcRequest != null) {
-            if (mPrioritizedDcRequests.remove(dcRequest)) {
-                onEvaluate(REQUESTS_CHANGED, "netReleased");
-                collectReleaseNetworkMetrics(networkRequest);
-                if (VDBG) log("Removed DcRequest, size: " + mPrioritizedDcRequests.size());
-            }
-        }
-    }
-
-    private ApnConfigTypeRepository createApnRepository(NetworkRequest networkRequest) {
-        int phoneIdForRequest = phoneIdForRequest(networkRequest);
-        int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneIdForRequest);
-        CarrierConfigManager configManager = (CarrierConfigManager) mContext
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-
-        PersistableBundle carrierConfig;
-        if (configManager != null) {
-            carrierConfig = configManager.getConfigForSubId(subId);
-        } else {
-            carrierConfig = null;
-        }
-        return new ApnConfigTypeRepository(carrierConfig);
-    }
-
-    private void removeDefaultNetworkChangeCallback() {
-        removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK);
-        mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        mDefaultNetworkCallback.mSwitchReason =
-                TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
-        mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
     }
 
     private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) {
         mDefaultNetworkCallback.mExpectedSubId = expectedSubId;
         mDefaultNetworkCallback.mSwitchReason = reason;
-        mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
-        sendMessageDelayed(
-                obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK),
-                DEFAULT_NETWORK_CHANGE_TIMEOUT_MS);
     }
 
     private void collectRequestNetworkMetrics(NetworkRequest networkRequest) {
@@ -1040,6 +1087,190 @@
         }
     }
 
+    /**
+     * Called when service state changed.
+     */
+    private void onServiceStateChanged(int phoneId) {
+        Phone phone = findPhoneById(phoneId);
+        if (phone != null) {
+            int newRegState = phone.getServiceState()
+                    .getNetworkRegistrationInfo(
+                            NetworkRegistrationInfo.DOMAIN_PS,
+                            AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .getRegistrationState();
+            if (newRegState != mPhoneStates[phoneId].dataRegState) {
+                mPhoneStates[phoneId].dataRegState = newRegState;
+                logl("onServiceStateChanged:phoneId:" + phoneId + " dataReg-> "
+                        + NetworkRegistrationInfo.registrationStateToString(newRegState));
+                if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+                    sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+                }
+            }
+        }
+    }
+
+    /**
+     * Evaluate if auto switch is suitable at the moment.
+     */
+    private void evaluateIfAutoSwitchIsNeeded() {
+        // auto data switch feature is disabled from server
+        if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return;
+        // check is valid DSDS
+        if (!mSubscriptionController.isActiveSubId(mPrimaryDataSubId)
+                || mSubscriptionController.getActiveSubIdList(true).length <= 1) {
+            return;
+        }
+        int primaryPhoneId = mSubscriptionController.getPhoneId(mPrimaryDataSubId);
+
+        log("evaluateIfAutoSwitchIsNeeded: primaryPhoneId: " + primaryPhoneId
+                + " preferredPhoneId: " + mPreferredDataPhoneId);
+        Phone primaryDataPhone = findPhoneById(primaryPhoneId);
+        Phone secondaryDataPhone;
+        if (primaryDataPhone != null) {
+
+            if (mPreferredDataPhoneId == primaryPhoneId) {
+                // on primary data sub
+                int candidateSubId = getAutoSwitchTargetSubIdIfExists();
+                if (candidateSubId != INVALID_SUBSCRIPTION_ID) {
+                    startAutoDataSwitchStabilityCheck(candidateSubId, true);
+                } else {
+                    cancelPendingAutoDataSwitch();
+                }
+            } else if ((secondaryDataPhone = findPhoneById(mPreferredDataPhoneId)) != null) {
+                // on secondary data sub
+
+                if (!primaryDataPhone.isUserDataEnabled()
+                        || !secondaryDataPhone.isDataAllowed()) {
+                    // immediately switch back if user setting changes
+                    mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
+                    evaluateIfImmediateDataSwitchIsNeeded("User disabled data settings",
+                            DataSwitch.Reason.DATA_SWITCH_REASON_AUTO);
+                    return;
+                }
+
+                NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
+                        .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
+                if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
+                        .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+                    log("evaluateIfAutoSwitchIsNeeded:"
+                            + "Default network is active on non-cellular transport");
+                    startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
+                    return;
+                }
+
+                if (mPhoneStates[secondaryDataPhone.getPhoneId()].dataRegState
+                        != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+                    // secondary phone lost its HOME availability
+                    startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
+                    return;
+                }
+
+                if (isInService(mPhoneStates[primaryPhoneId])) {
+                    // primary becomes available
+                    startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, true);
+                    return;
+                }
+
+                // cancel any previous attempts of switching back to primary
+                cancelPendingAutoDataSwitch();
+            }
+
+        }
+    }
+
+    /**
+     * @param phoneState The phone state to check
+     * @return {@code true} if the phone state is considered in service.
+     */
+    private boolean isInService(@NonNull PhoneState phoneState) {
+        return phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+                || phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * Called when the current environment suits auto data switch.
+     * Start pre-switch validation if the current environment suits auto data switch for
+     * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS.
+     * @param targetSubId the target sub Id.
+     * @param needValidation {@code true} if validation is needed.
+     */
+    private void startAutoDataSwitchStabilityCheck(int targetSubId, boolean needValidation) {
+        log("startAutoDataSwitchStabilityCheck: targetSubId=" + targetSubId
+                + " needValidation=" + needValidation);
+        if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) {
+            sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetSubId,
+                            0/*placeholder*/,
+                            needValidation),
+                    mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+        }
+    }
+
+    /**
+     * Cancel any auto switch attempts when the current environment is not suitable for auto switch.
+     */
+    private void cancelPendingAutoDataSwitch() {
+        mAutoSwitchRetryFailedCount = 0;
+        removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
+        if (mValidator.isValidating()) {
+            mValidator.stopValidation();
+
+            removeMessages(EVENT_NETWORK_VALIDATION_DONE);
+            removeMessages(EVENT_NETWORK_AVAILABLE);
+            mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+            mPendingSwitchNeedValidation = false;
+        }
+    }
+
+    /**
+     * Called when consider switching from primary default data sub to another data sub.
+     * @return the target subId if a suitable candidate is found, otherwise return
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     */
+    private int getAutoSwitchTargetSubIdIfExists() {
+        int primaryPhoneId = mSubscriptionController.getPhoneId(mPrimaryDataSubId);
+        Phone primaryDataPhone = findPhoneById(primaryPhoneId);
+
+        if (primaryDataPhone == null) {
+            log("getAutoSwitchTargetSubId: no sim loaded");
+            return INVALID_SUBSCRIPTION_ID;
+        }
+
+        if (!primaryDataPhone.isUserDataEnabled()) {
+            log("getAutoSwitchTargetSubId: user disabled data");
+            return INVALID_SUBSCRIPTION_ID;
+        }
+
+        NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
+                .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
+        if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
+                .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            // Exists other active default transport
+            log("getAutoSwitchTargetSubId: Default network is active on non-cellular transport");
+            return INVALID_SUBSCRIPTION_ID;
+        }
+
+        // check whether primary and secondary signal status worth switching
+        if (isInService(mPhoneStates[primaryPhoneId])) {
+            log("getAutoSwitchTargetSubId: primary is in service");
+            return INVALID_SUBSCRIPTION_ID;
+        }
+        for (int phoneId = 0; phoneId < mPhoneStates.length; phoneId++) {
+            if (phoneId != primaryPhoneId) {
+                // the alternative phone must have HOME availability
+                if (mPhoneStates[phoneId].dataRegState
+                        == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+                    log("getAutoSwitchTargetSubId: found phone " + phoneId + " in HOME service");
+                    Phone secondaryDataPhone = findPhoneById(phoneId);
+                    if (secondaryDataPhone != null && // check auto switch feature enabled
+                            secondaryDataPhone.isDataAllowed()) {
+                        return secondaryDataPhone.getSubId();
+                    }
+                }
+            }
+        }
+        return INVALID_SUBSCRIPTION_ID;
+    }
+
     private TelephonyManager getTm() {
         return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
     }
@@ -1081,6 +1312,9 @@
             if (sub != mPhoneSubscriptions[i]) {
                 sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]);
                 sb.append("->").append(sub);
+                if (mAutoSelectedDataSubId == mPhoneSubscriptions[i]) {
+                    mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
+                }
                 mPhoneSubscriptions[i] = sub;
                 diffDetected = true;
             }
@@ -1110,7 +1344,7 @@
                     .append("->").append(mPreferredDataPhoneId);
             diffDetected = true;
         } else if (oldPreferredDataSubId != mPreferredDataSubId.get()) {
-            log("SIM refresh, notify dds change");
+            logl("SIM refresh, notify dds change");
             // Inform connectivity about the active data phone
             notifyPreferredDataSubIdChanged();
         }
@@ -1119,7 +1353,7 @@
         // DDS are out of sync after APM, AP should force DDS when radio on. long term solution
         // should be having API to query preferred data modem to detect the out-of-sync scenarios.
         if (diffDetected || EVALUATION_REASON_RADIO_ON.equals(reason)) {
-            log("evaluating due to " + sb.toString());
+            logl("evaluating due to " + sb);
             if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
                 // With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach.
                 // So marking all phone as active, and the phone with mPreferredDataPhoneId
@@ -1147,22 +1381,12 @@
                     }
 
                     if (newActivePhones.size() < mMaxDataAttachModemCount) {
-                        if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
-                            for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
-                                int phoneIdForRequest = phoneIdForRequest(networkRequest);
-                                if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
-                                if (newActivePhones.contains(phoneIdForRequest)) continue;
-                                newActivePhones.add(phoneIdForRequest);
-                                if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
-                            }
-                        } else {
-                            for (DcRequest dcRequest : mPrioritizedDcRequests) {
-                                int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
-                                if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
-                                if (newActivePhones.contains(phoneIdForRequest)) continue;
-                                newActivePhones.add(phoneIdForRequest);
-                                if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
-                            }
+                        for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
+                            int phoneIdForRequest = phoneIdForRequest(networkRequest);
+                            if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
+                            if (newActivePhones.contains(phoneIdForRequest)) continue;
+                            newActivePhones.add(phoneIdForRequest);
+                            if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
                         }
                     }
 
@@ -1175,7 +1399,7 @@
 
                 if (VDBG) {
                     log("mPrimaryDataSubId = " + mPrimaryDataSubId);
-                    log("mOpptDataSubId = " + mOpptDataSubId);
+                    log("mAutoSelectedDataSubId = " + mAutoSelectedDataSubId);
                     for (int i = 0; i < mActiveModemCount; i++) {
                         log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
                     }
@@ -1200,6 +1424,8 @@
 
     protected static class PhoneState {
         public volatile boolean active = false;
+        public @NetworkRegistrationInfo.RegistrationState int dataRegState =
+                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
         public long lastRequested = 0;
     }
 
@@ -1217,7 +1443,7 @@
         PhoneState state = mPhoneStates[phoneId];
         if (state.active == active) return;
         state.active = active;
-        log((active ? "activate " : "deactivate ") + phoneId);
+        logl((active ? "activate " : "deactivate ") + phoneId);
         state.lastRequested = System.currentTimeMillis();
         sendRilCommands(phoneId);
     }
@@ -1264,7 +1490,7 @@
 
     protected void sendRilCommands(int phoneId) {
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            log("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId);
+            logl("sendRilCommands: skip dds switch due to invalid phoneId=" + phoneId);
             return;
         }
 
@@ -1276,7 +1502,7 @@
             }
         } else if (phoneId == mPreferredDataPhoneId) {
             // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId
-            log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
+            logl("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
             mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, message);
         }
     }
@@ -1286,18 +1512,13 @@
                 .getNumberOfModemsWithSimultaneousDataConnections();
         if (mMaxDataAttachModemCount != newMaxDataAttachModemCount) {
             mMaxDataAttachModemCount = newMaxDataAttachModemCount;
-            log("Max active phones changed to " + mMaxDataAttachModemCount);
+            logl("Max active phones changed to " + mMaxDataAttachModemCount);
             onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged");
         }
     }
 
-    // Merge phoneIdForRequest(NetworkRequest netRequest) after Phone.isUsingNewDataStack() is
-    // cleaned up.
     private int phoneIdForRequest(TelephonyNetworkRequest networkRequest) {
-        return phoneIdForRequest(networkRequest.getNativeNetworkRequest());
-    }
-
-    private int phoneIdForRequest(NetworkRequest netRequest) {
+        NetworkRequest netRequest = networkRequest.getNativeNetworkRequest();
         int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
 
         if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId;
@@ -1342,8 +1563,8 @@
     }
 
     private int getSubIdForDefaultNetworkRequests() {
-        if (mSubscriptionController.isActiveSubId(mOpptDataSubId)) {
-            return mOpptDataSubId;
+        if (mSubscriptionController.isActiveSubId(mAutoSelectedDataSubId)) {
+            return mAutoSelectedDataSubId;
         } else {
             return mPrimaryDataSubId;
         }
@@ -1353,15 +1574,14 @@
     // requests.
     protected void updatePreferredDataPhoneId() {
         Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall);
+        // check user enabled data on the default phone
+        int defaultDataPhoneId = SubscriptionController.getInstance().getPhoneId(mPrimaryDataSubId);
+        Phone defaultDataPhone = findPhoneById(defaultDataPhoneId);
         boolean isDataEnabled = false;
-        if (voicePhone != null) {
-            if (voicePhone.isUsingNewDataStack()) {
-                isDataEnabled = voicePhone.getDataSettingsManager()
-                        .isDataEnabled(ApnSetting.TYPE_DEFAULT);
-            } else {
-                isDataEnabled = voicePhone.getDataEnabledSettings()
-                        .isDataEnabled(ApnSetting.TYPE_DEFAULT);
-            }
+        if (voicePhone != null && defaultDataPhone != null
+                && defaultDataPhone.isUserDataEnabled()) {
+            // check voice during call feature is enabled
+            isDataEnabled = voicePhone.isDataAllowed();
         }
 
         if (mEmergencyOverride != null && findPhoneById(mEmergencyOverride.mPhoneId) != null) {
@@ -1369,14 +1589,13 @@
             // emergency.
             // TODO: Provide a notification to the user that metered data is currently being
             // used during this period.
-            log("updatePreferredDataPhoneId: preferred data overridden for emergency."
+            logl("updatePreferredDataPhoneId: preferred data overridden for emergency."
                     + " phoneId = " + mEmergencyOverride.mPhoneId);
             mPreferredDataPhoneId = mEmergencyOverride.mPhoneId;
         } else if (isDataEnabled) {
             // If a phone is in call and user enabled its mobile data, we
             // should switch internet connection to it. Because the other modem
             // will lose data connection anyway.
-            // TODO: validate network first.
             mPreferredDataPhoneId = mPhoneIdInVoiceCall;
         } else {
             int subId = getSubIdForDefaultNetworkRequests();
@@ -1400,12 +1619,12 @@
 
     protected void transitionToEmergencyPhone() {
         if (mActiveModemCount <= 0) {
-            log("No phones: unable to reset preferred phone for emergency");
+            logl("No phones: unable to reset preferred phone for emergency");
             return;
         }
 
         if (mPreferredDataPhoneId != DEFAULT_EMERGENCY_PHONE_ID) {
-            log("No active subscriptions: resetting preferred phone to 0 for emergency");
+            logl("No active subscriptions: resetting preferred phone to 0 for emergency");
             mPreferredDataPhoneId = DEFAULT_EMERGENCY_PHONE_ID;
         }
 
@@ -1488,45 +1707,67 @@
      */
     private void setOpportunisticDataSubscription(int subId, boolean needValidation,
             ISetOpportunisticDataCallback callback) {
-        if (!mSubscriptionController.isActiveSubId(subId)
-                && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-            log("Can't switch data to inactive subId " + subId);
+        validate(subId, needValidation,
+                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS, callback);
+    }
+
+    /**
+     * Try setup a new internet connection on the subId that's pending validation. If the validation
+     * succeeds, this subId will be evaluated for being the preferred data subId; If fails, nothing
+     * happens.
+     * Callback will be updated with the validation result.
+     *
+     * @param subId Sub Id that's pending switch, awaiting validation.
+     * @param needValidation {@code false} if switch to the subId even if validation fails.
+     * @param switchReason The switch reason for this validation
+     * @param callback Optional - specific for external opportunistic sub validation request.
+     */
+    private void validate(int subId, boolean needValidation, int switchReason,
+            @Nullable ISetOpportunisticDataCallback callback) {
+        logl("Validate subId " + subId + " due to " + switchReasonToString(switchReason)
+                + " needValidation=" + needValidation);
+        int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+                ? mPrimaryDataSubId : subId;
+        if (!mSubscriptionController.isActiveSubId(subIdToValidate)) {
+            logl("Can't switch data to inactive subId " + subIdToValidate);
+            if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+                // the default data sub is not selected yet, store the intent of switching to
+                // default subId once it becomes available.
+                mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+            }
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
             return;
         }
 
-        // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId
-        // if queued.
-        removeMessages(EVENT_NETWORK_VALIDATION_DONE);
-        removeMessages(EVENT_NETWORK_AVAILABLE);
-
-        int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
-                ? mPrimaryDataSubId : subId;
-
-        mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
-
         if (mValidator.isValidating()) {
             mValidator.stopValidation();
             sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
             mSetOpptSubCallback = null;
         }
 
-        if (subId == mOpptDataSubId) {
+        // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previous subId
+        // if queued.
+        removeMessages(EVENT_NETWORK_VALIDATION_DONE);
+        removeMessages(EVENT_NETWORK_AVAILABLE);
+
+        mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+
+        if (subIdToValidate == mPreferredDataSubId.get()) {
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
             return;
         }
 
-        logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
+        mLastAutoSelectedSwitchReason = switchReason;
+        logDataSwitchEvent(subIdToValidate,
                 TelephonyEvent.EventState.EVENT_STATE_START,
-                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
-        registerDefaultNetworkChangeCallback(
-                subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
-                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
+                switchReason);
+        registerDefaultNetworkChangeCallback(subIdToValidate,
+                switchReason);
 
         // If validation feature is not supported, set it directly. Otherwise,
         // start validation on the subscription first.
         if (!mValidator.isValidationFeatureSupported()) {
-            setOpportunisticSubscriptionInternal(subId);
+            setAutoSelectedDataSubIdInternal(subIdToValidate);
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
             return;
         }
@@ -1563,36 +1804,46 @@
         try {
             callback.onComplete(result);
         } catch (RemoteException exception) {
-            log("RemoteException " + exception);
+            logl("RemoteException " + exception);
         }
     }
 
     /**
-     * Set opportunistic data subscription.
+     * Evaluate whether the specified sub Id can be set to be the preferred data sub Id.
+     *
+     * @param subId The subId that we tried to validate: could possibly be unvalidated if validation
+     * feature is not supported.
      */
-    private void setOpportunisticSubscriptionInternal(int subId) {
-        if (mOpptDataSubId != subId) {
-            mOpptDataSubId = subId;
-            onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed");
+    private void setAutoSelectedDataSubIdInternal(int subId) {
+        if (mAutoSelectedDataSubId != subId) {
+            mAutoSelectedDataSubId = subId;
+            onEvaluate(REQUESTS_UNCHANGED, switchReasonToString(mLastAutoSelectedSwitchReason));
         }
     }
 
     private void confirmSwitch(int subId, boolean confirm) {
-        log("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled."));
+        logl("confirmSwitch: subId " + subId + (confirm ? " confirmed." : " cancelled."));
         int resultForCallBack;
         if (!mSubscriptionController.isActiveSubId(subId)) {
-            log("confirmSwitch: subId " + subId + " is no longer active");
+            logl("confirmSwitch: subId " + subId + " is no longer active");
             resultForCallBack = SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
+            mAutoSwitchRetryFailedCount = 0;
         } else if (!confirm) {
             resultForCallBack = SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+
+            // retry for auto data switch validation failure
+            if (mLastAutoSelectedSwitchReason == DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
+                scheduleAutoSwitchRetryEvaluation();
+                mAutoSwitchRetryFailedCount++;
+            }
         } else {
-            if (mSubscriptionController.isOpportunistic(subId)) {
-                setOpportunisticSubscriptionInternal(subId);
+            if (subId == mPrimaryDataSubId) {
+                setAutoSelectedDataSubIdInternal(DEFAULT_SUBSCRIPTION_ID);
             } else {
-                // Switching data back to primary subscription.
-                setOpportunisticSubscriptionInternal(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+                setAutoSelectedDataSubIdInternal(subId);
             }
             resultForCallBack = SET_OPPORTUNISTIC_SUB_SUCCESS;
+            mAutoSwitchRetryFailedCount = 0;
         }
 
         // Trigger callback if needed
@@ -1601,6 +1852,23 @@
         mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
     }
 
+    /**
+     * Schedule auto data switch evaluation retry if haven't reached the max retry count.
+     */
+    private void scheduleAutoSwitchRetryEvaluation() {
+        if (mAutoSwitchRetryFailedCount < mAutoDataSwitchValidationMaxRetry) {
+            if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+                sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH),
+                        mAutoDataSwitchAvailabilityStabilityTimeThreshold
+                                << mAutoSwitchRetryFailedCount);
+            }
+        } else {
+            logl("scheduleAutoSwitchEvaluation: reached max auto switch retry count "
+                    + mAutoDataSwitchValidationMaxRetry);
+            mAutoSwitchRetryFailedCount = 0;
+        }
+    }
+
     private void onNetworkAvailable(int subId, Network network) {
         log("onNetworkAvailable: on subId " + subId);
         // Do nothing unless pending switch matches target subId and it doesn't require
@@ -1613,7 +1881,7 @@
     }
 
     private void onValidationDone(int subId, boolean passed) {
-        log("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId);
+        logl("onValidationDone: " + (passed ? "passed" : "failed") + " on subId " + subId);
         if (mPendingSwitchSubId == INVALID_SUBSCRIPTION_ID || mPendingSwitchSubId != subId) return;
 
         // If validation failed and mPendingSwitch.mNeedValidation is false, we still confirm
@@ -1639,7 +1907,7 @@
      */
     public void trySetOpportunisticDataSubscription(int subId, boolean needValidation,
             ISetOpportunisticDataCallback callback) {
-        log("Try set opportunistic data subscription to subId " + subId
+        logl("Try set opportunistic data subscription to subId " + subId
                 + (needValidation ? " with " : " without ") + "validation");
         PhoneSwitcher.this.obtainMessage(EVENT_OPPT_DATA_SUB_CHANGED,
                 subId, needValidation ? 1 : 0, callback).sendToTarget();
@@ -1660,21 +1928,38 @@
                 ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA;
     }
 
-    public int getOpportunisticDataSubscriptionId() {
-        return mOpptDataSubId;
-    }
-
     public int getPreferredDataPhoneId() {
         return mPreferredDataPhoneId;
     }
 
+    /**
+     * Log debug messages and also log into the local log.
+     * @param l debug messages
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    protected void log(String l) {
-        Rlog.d(LOG_TAG, l);
+    protected void logl(String l) {
+        log(l);
         mLocalLog.log(l);
     }
 
     /**
+     * Log debug messages.
+     * @param s debug messages
+     */
+    private void log(@NonNull String s) {
+        Rlog.d(LOG_TAG, s);
+    }
+
+    /**
+     * Log debug error messages.
+     * @param s debug messages
+     */
+    private void loge(@NonNull String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+
+
+    /**
      * Convert data switch reason into string.
      *
      * @param reason The switch reason.
@@ -1690,6 +1975,8 @@
                 return "IN_CALL";
             case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_CBRS:
                 return "CBRS";
+            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_AUTO:
+                return "AUTO";
             default: return "UNKNOWN(" + reason + ")";
         }
     }
@@ -1720,7 +2007,7 @@
      * @param reason The switching reason.
      */
     private void logDataSwitchEvent(int subId, int state, int reason) {
-        log("Data switch event. subId=" + subId + ", state=" + switchStateToString(state)
+        logl("Data switch event. subId=" + subId + ", state=" + switchStateToString(state)
                 + ", reason=" + switchReasonToString(reason));
         DataSwitch dataSwitch = new DataSwitch();
         dataSwitch.state = state;
@@ -1734,7 +2021,7 @@
     protected void notifyPreferredDataSubIdChanged() {
         TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) mContext
                 .getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
-        log("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get());
+        logl("notifyPreferredDataSubIdChanged to " + mPreferredDataSubId.get());
         telephonyRegistryManager.notifyActiveDataSubIdChanged(mPreferredDataSubId.get());
     }
 
@@ -1745,6 +2032,13 @@
         return mPreferredDataSubId.get();
     }
 
+    /**
+     * @return The auto selected data subscription id.
+     */
+    public int getAutoSelectedDataSubId() {
+        return mAutoSelectedDataSubId;
+    }
+
     // TODO (b/148396668): add an internal callback method to monitor phone capability change,
     // and hook this call to that callback.
     private void onPhoneCapabilityChanged(PhoneCapability capability) {
@@ -1759,8 +2053,10 @@
         for (int i = 0; i < mActiveModemCount; i++) {
             PhoneState ps = mPhoneStates[i];
             c.setTimeInMillis(ps.lastRequested);
-            pw.println("PhoneId(" + i + ") active=" + ps.active + ", lastRequest=" +
-                    (ps.lastRequested == 0 ? "never" :
+            pw.println("PhoneId(" + i + ") active=" + ps.active + ", dataRegState="
+                    + NetworkRegistrationInfo.registrationStateToString(ps.dataRegState)
+                    + ", lastRequest="
+                    + (ps.lastRequested == 0 ? "never" :
                      String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)));
         }
         pw.println("mPreferredDataPhoneId=" + mPreferredDataPhoneId);
@@ -1769,13 +2065,17 @@
         pw.println("DefaultDataPhoneId=" + mSubscriptionController.getPhoneId(
                 mSubscriptionController.getDefaultDataSubId()));
         pw.println("mPrimaryDataSubId=" + mPrimaryDataSubId);
-        pw.println("mOpptDataSubId=" + mOpptDataSubId);
+        pw.println("mAutoSelectedDataSubId=" + mAutoSelectedDataSubId);
         pw.println("mIsRegisteredForImsRadioTechChange=" + mIsRegisteredForImsRadioTechChange);
         pw.println("mPendingSwitchNeedValidation=" + mPendingSwitchNeedValidation);
         pw.println("mMaxDataAttachModemCount=" + mMaxDataAttachModemCount);
         pw.println("mActiveModemCount=" + mActiveModemCount);
         pw.println("mPhoneIdInVoiceCall=" + mPhoneIdInVoiceCall);
         pw.println("mCurrentDdsSwitchFailure=" + mCurrentDdsSwitchFailure);
+        pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
+                + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+        pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry);
+        pw.println("mDisplayedAutoSwitchNotification=" + mDisplayedAutoSwitchNotification);
         pw.println("Local logs:");
         pw.increaseIndent();
         mLocalLog.dump(fd, pw, args);
@@ -1793,36 +2093,96 @@
         boolean commandSuccess = ar != null && ar.exception == null;
         int phoneId = (int) ar.userObj;
         if (mEmergencyOverride != null) {
-            log("Emergency override result sent = " + commandSuccess);
+            logl("Emergency override result sent = " + commandSuccess);
             mEmergencyOverride.sendOverrideCompleteCallbackResultAndClear(commandSuccess);
             // Do not retry , as we do not allow changes in onEvaluate during an emergency
             // call. When the call ends, we will start the countdown to remove the override.
         } else if (!commandSuccess) {
-            log("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception);
+            logl("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception);
             if (ar.exception instanceof CommandException) {
                 CommandException.Error error = ((CommandException)
                         (ar.exception)).getCommandError();
                 mCurrentDdsSwitchFailure.get(phoneId).add(error);
                 if (error == CommandException.Error.OP_NOT_ALLOWED_DURING_VOICE_CALL) {
-                    log("onDdsSwitchResponse: Wait for call end indication");
+                    logl("onDdsSwitchResponse: Wait for call end indication");
                     return;
                 } else if (error == CommandException.Error.INVALID_SIM_STATE) {
                     /* If there is a attach failure due to sim not ready then
                     hold the retry until sim gets ready */
-                    log("onDdsSwitchResponse: Wait for SIM to get READY");
+                    logl("onDdsSwitchResponse: Wait for SIM to get READY");
                     return;
                 }
             }
-            log("onDdsSwitchResponse: Scheduling DDS switch retry");
+            logl("onDdsSwitchResponse: Scheduling DDS switch retry");
             sendMessageDelayed(Message.obtain(this, EVENT_MODEM_COMMAND_RETRY,
                         phoneId), MODEM_COMMAND_RETRY_PERIOD_MS);
             return;
         }
-        if (commandSuccess) log("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
+        if (commandSuccess) logl("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
         mCurrentDdsSwitchFailure.get(phoneId).clear();
         // Notify all registrants
         mActivePhoneRegistrants.notifyRegistrants();
         notifyPreferredDataSubIdChanged();
+        displayAutoDataSwitchNotification();
+    }
+
+    /**
+     * Display a notification the first time auto data switch occurs.
+     */
+    private void displayAutoDataSwitchNotification() {
+        NotificationManager notificationManager = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (mDisplayedAutoSwitchNotification) {
+            // cancel posted notification if any exist
+            log("displayAutoDataSwitchNotification: canceling any notifications for subId "
+                    + mAutoSelectedDataSubId);
+            notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+                    AUTO_DATA_SWITCH_NOTIFICATION_ID);
+            return;
+        }
+        // proceed only the first time auto data switch occurs
+        if (mLastAutoSelectedSwitchReason != DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
+            log("displayAutoDataSwitchNotification: Ignore DDS switch due to "
+                    + switchReasonToString(mLastAutoSelectedSwitchReason));
+            return;
+        }
+        SubscriptionInfo subInfo = mSubscriptionController.getSubscriptionInfo(
+                mAutoSelectedDataSubId);
+        if (subInfo == null || subInfo.isOpportunistic()) {
+            loge("displayAutoDataSwitchNotification: unexpected " + subInfo);
+            return;
+        }
+        logl("displayAutoDataSwitchNotification: display for subId=" + mAutoSelectedDataSubId);
+        // "Mobile network settings" screen / dialog
+        Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+        final Bundle fragmentArgs = new Bundle();
+        // Special contract for Settings to highlight permission row
+        fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
+        intent.putExtra(Settings.EXTRA_SUB_ID, mAutoSelectedDataSubId);
+        intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+        PendingIntent contentIntent = PendingIntent.getActivity(
+                mContext, mAutoSelectedDataSubId, intent, PendingIntent.FLAG_IMMUTABLE);
+
+        CharSequence activeCarrierName = subInfo.getDisplayName();
+        CharSequence contentTitle = mContext.getString(
+                com.android.internal.R.string.auto_data_switch_title, activeCarrierName);
+        CharSequence contentText = mContext.getText(
+                com.android.internal.R.string.auto_data_switch_content);
+
+        final Notification notif = new Notification.Builder(mContext)
+                .setContentTitle(contentTitle)
+                .setContentText(contentText)
+                .setSmallIcon(android.R.drawable.stat_sys_warning)
+                .setColor(mContext.getResources().getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+                .setContentIntent(contentIntent)
+                .setStyle(new Notification.BigTextStyle().bigText(contentText))
+                .build();
+        notificationManager.notify(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+                AUTO_DATA_SWITCH_NOTIFICATION_ID, notif);
+        mDisplayedAutoSwitchNotification = true;
     }
 
     private boolean isPhoneIdValidForRetry(int phoneId) {
@@ -1832,25 +2192,11 @@
         if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) {
             return true;
         } else {
-            if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
-                if (mNetworkRequestList.isEmpty()) return false;
-                for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
-                    phoneIdForRequest = phoneIdForRequest(networkRequest);
-                    if (phoneIdForRequest == phoneId) {
-                        return true;
-                    }
-                }
-            } else {
-                if (mPrioritizedDcRequests.size() == 0) {
-                    return false;
-                }
-                for (DcRequest dcRequest : mPrioritizedDcRequests) {
-                    if (dcRequest != null) {
-                        phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
-                        if (phoneIdForRequest == phoneId) {
-                            return true;
-                        }
-                    }
+            if (mNetworkRequestList.isEmpty()) return false;
+            for (TelephonyNetworkRequest networkRequest : mNetworkRequestList) {
+                phoneIdForRequest = phoneIdForRequest(networkRequest);
+                if (phoneIdForRequest == phoneId) {
+                    return true;
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
index bde24df..ac04627 100644
--- a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
+++ b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
@@ -54,8 +54,7 @@
     private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3;
 
     private final @NonNull String mLogTag;
-    // TODO: Change this to TelephonyNetworkAgent
-    private final @NonNull NotifyQosSessionInterface mNetworkAgent;
+    private final @NonNull TelephonyNetworkAgent mNetworkAgent;
     private final @NonNull Map<Integer, QosBearerSession> mQosBearerSessions;
     private final @NonNull RcsStats mRcsStats;
 
@@ -81,12 +80,20 @@
         /**
          * Filter using the remote address.
          *
-         * @param address The local address.
+         * @param address The remote address.
          * @param startPort Starting port.
          * @param endPort Ending port.
          * @return {@code true} if matches, {@code false} otherwise.
          */
         boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort);
+
+        /**
+         * Filter using the protocol
+         *
+         * @param protocol ID
+         * @return {@code true} if matches, {@code false} otherwise.
+         */
+        boolean matchesProtocol(int protocol);
     }
 
     /**
@@ -95,8 +102,7 @@
      * @param networkAgent The network agent to send events to.
      * @param phone The phone instance.
      */
-    public QosCallbackTracker(@NonNull NotifyQosSessionInterface networkAgent,
-            @NonNull Phone phone) {
+    public QosCallbackTracker(@NonNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone) {
         mQosBearerSessions = new HashMap<>();
         mCallbacksToFilter = new HashMap<>();
         mNetworkAgent = networkAgent;
@@ -104,40 +110,41 @@
         mRcsStats = RcsStats.getInstance();
         mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId();
 
-        if (phone.isUsingNewDataStack()) {
-            //TODO: Replace the NetworkAgent in the constructor with TelephonyNetworkAgent
-            //  after mPhone.isUsingNewDataStack() check is removed.
-            ((TelephonyNetworkAgent) networkAgent).registerCallback(
-                    new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) {
-                        @Override
-                        public void onQosCallbackRegistered(int qosCallbackId,
-                                @NonNull QosFilter filter) {
-                            addFilter(qosCallbackId,
-                                    new QosCallbackTracker.IFilter() {
-                                        @Override
-                                        public boolean matchesLocalAddress(
-                                                @NonNull InetAddress address, int startPort,
-                                                int endPort) {
-                                            return filter.matchesLocalAddress(address, startPort,
-                                                    endPort);
-                                        }
+        networkAgent.registerCallback(
+                new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) {
+                    @Override
+                    public void onQosCallbackRegistered(int qosCallbackId,
+                            @NonNull QosFilter filter) {
+                        addFilter(qosCallbackId,
+                                new QosCallbackTracker.IFilter() {
+                                    @Override
+                                    public boolean matchesLocalAddress(
+                                            @NonNull InetAddress address, int startPort,
+                                            int endPort) {
+                                        return filter.matchesLocalAddress(address, startPort,
+                                                endPort);
+                                    }
 
-                                        @Override
-                                        public boolean matchesRemoteAddress(
-                                                @NonNull InetAddress address, int startPort,
-                                                int endPort) {
-                                            return filter.matchesRemoteAddress(address, startPort,
-                                                    endPort);
-                                        }
-                                    });
-                        }
+                                    @Override
+                                    public boolean matchesRemoteAddress(
+                                            @NonNull InetAddress address, int startPort,
+                                            int endPort) {
+                                        return filter.matchesRemoteAddress(address, startPort,
+                                                endPort);
+                                    }
 
-                        @Override
-                        public void onQosCallbackUnregistered(int qosCallbackId) {
+                                    @Override
+                                    public boolean matchesProtocol(int protocol) {
+                                        return filter.matchesProtocol(protocol);
+                                    }
+                                });
+                    }
 
-                        }
-                    });
-        }
+                    @Override
+                    public void onQosCallbackUnregistered(int qosCallbackId) {
+
+                    }
+                });
     }
 
     /**
@@ -338,6 +345,21 @@
         return result;
     }
 
+    private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter,
+            final @NonNull IFilter filter, boolean hasMatchedFilter) {
+        boolean result = false;
+        int protocol = sessionFilter.getProtocol();
+        if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP
+                || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) {
+            result = filter.matchesProtocol(protocol);
+        } else {
+            // FWK currently doesn't support filtering based on protocol ID ESP & AH. We will follow
+            // match results of other filters.
+            result = hasMatchedFilter;
+        }
+        return result;
+    }
+
     private QosBearerFilter getFilterByPrecedence(
             @Nullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter) {
         // Find for the highest precedence filter, lower the value is the higher the precedence
@@ -368,6 +390,15 @@
                     hasMatchedFilter = true;
                 }
             }
+
+            if (sessionFilter.getProtocol() != QosBearerFilter.QOS_PROTOCOL_UNSPECIFIED) {
+                if (!matchesByProtocol(sessionFilter, filter, hasMatchedFilter)) {
+                    unMatched = true;
+                } else {
+                    hasMatchedFilter = true;
+                }
+            }
+
             if (!unMatched && hasMatchedFilter) {
                 qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
             }
@@ -395,7 +426,7 @@
                             qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
                             qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
                             remoteAddresses);
-            mNetworkAgent.notifyQosSessionAvailable(
+            mNetworkAgent.sendQosSessionAvailable(
                     callbackId, session.getQosBearerSessionId(), epsBearerAttr);
         } else {
             NrQos qos = (NrQos) session.getQos();
@@ -406,7 +437,7 @@
                             qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
                             qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
                             qos.getAveragingWindow(), remoteAddresses);
-            mNetworkAgent.notifyQosSessionAvailable(
+            mNetworkAgent.sendQosSessionAvailable(
                     callbackId, session.getQosBearerSessionId(), nrQosAttr);
         }
 
@@ -417,7 +448,7 @@
     }
 
     private void sendSessionLost(int callbackId, @NonNull QosBearerSession session) {
-        mNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(),
+        mNetworkAgent.sendQosSessionLost(callbackId, session.getQosBearerSessionId(),
                 session.getQos() instanceof EpsQos
                         ? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER);
         log("sendSessionLost, callbackId=" + callbackId);
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
index b74396b..78c555c 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
@@ -26,7 +26,6 @@
 import android.net.NetworkProvider;
 import android.net.NetworkScore;
 import android.net.QosFilter;
-import android.net.QosSessionAttributes;
 import android.net.Uri;
 import android.os.Looper;
 import android.util.ArraySet;
@@ -47,9 +46,8 @@
  * for telephony to propagate network related information to the connectivity service. It always
  * has an associated parent {@link DataNetwork}.
  */
-public class TelephonyNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface {
+public class TelephonyNetworkAgent extends NetworkAgent {
     private final String mLogTag;
-    private final Phone mPhone;
     private final LocalLog mLocalLog = new LocalLog(128);
 
     /** The parent data network. */
@@ -166,7 +164,6 @@
         mDataNetwork = dataNetwork;
         mNetworkAgentConfig = config;
         mTelephonyNetworkAgentCallbacks.add(callback);
-        mPhone = phone;
         mId = getNetwork().getNetId();
         mLogTag = "TNA-" + mId;
 
@@ -298,37 +295,6 @@
     }
 
     /**
-     * Sends the attributes of Qos Session back to the Application. This method is create for
-     * Mockito to mock since
-     * {@link NetworkAgent#sendQosSessionAvailable(int, int, QosSessionAttributes)} is
-     * {@code final} that can't be mocked.
-     *
-     * @param qosCallbackId the callback id that the session belongs to.
-     * @param sessionId the unique session id across all Qos Sessions.
-     * @param attributes the attributes of the Qos Session.
-     */
-    @Override
-    public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
-            @NonNull final QosSessionAttributes attributes) {
-        super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
-    }
-
-    /**
-     * Sends event that the Qos Session was lost. This method is create for Mockito to mock
-     * since {@link NetworkAgent#sendQosSessionLost(int, int, int)} is {@code final} that can't be
-     * mocked..
-     *
-     * @param qosCallbackId the callback id that the session belongs to.
-     * @param sessionId the unique session id across all Qos Sessions.
-     * @param qosSessionType the session type {@code QosSession#QosSessionType}.
-     */
-    @Override
-    public void notifyQosSessionLost(final int qosCallbackId,
-            final int sessionId, final int qosSessionType) {
-        super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
-    }
-
-    /**
      * Abandon the network agent. This is used for telephony to re-create the network agent when
      * immutable capabilities got changed, where telephony calls {@link NetworkAgent#unregister()}
      * and then create another network agent with new capabilities. Abandon this network agent
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index 85f0ae1..e64dd9b 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -20,27 +20,16 @@
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
-import android.os.AsyncResult;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
 import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
 import android.util.LocalLog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.dataconnection.ApnContext;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
 import com.android.internal.telephony.metrics.NetworkRequestsStats;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
@@ -72,19 +61,15 @@
     public static final int EVENT_SUBSCRIPTION_CHANGED              = 2;
     private static final int EVENT_NETWORK_REQUEST                  = 3;
     private static final int EVENT_NETWORK_RELEASE                  = 4;
-    private static final int EVENT_DATA_HANDOVER_NEEDED             = 5;
-    private static final int EVENT_DATA_HANDOVER_COMPLETED          = 6;
 
     private final PhoneSwitcher mPhoneSwitcher;
     private final SubscriptionController mSubscriptionController;
     private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE);
 
-    // Key: network request. Value: the transport of DcTracker it applies to,
+    // Key: network request. Value: the transport of the network request applies to,
     // AccessNetworkConstants.TRANSPORT_TYPE_INVALID if not applied.
     private final Map<TelephonyNetworkRequest, Integer> mNetworkRequests = new HashMap<>();
 
-    private final Map<Message, HandoverParams> mPendingHandovers = new HashMap<>();
-
     private final Phone mPhone;
 
     private AccessNetworksManager mAccessNetworksManager;
@@ -112,10 +97,6 @@
 
         mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH,
                 null);
-        if (!phone.isUsingNewDataStack()) {
-            mPhone.getTransportManager().registerForHandoverNeededEvent(mInternalHandler,
-                    EVENT_DATA_HANDOVER_NEEDED);
-        }
 
         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
@@ -201,93 +182,18 @@
                     onReleaseNetworkFor(msg);
                     break;
                 }
-                case EVENT_DATA_HANDOVER_NEEDED: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    HandoverParams handoverParams = (HandoverParams) ar.result;
-                    onDataHandoverNeeded(handoverParams.apnType, handoverParams.targetTransport,
-                            handoverParams);
-                    break;
-                }
-                case EVENT_DATA_HANDOVER_COMPLETED: {
-                    Bundle bundle = msg.getData();
-                    NetworkRequest nr = bundle.getParcelable(
-                            DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
-                    boolean success = bundle.getBoolean(
-                            DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
-                    int transport = bundle.getInt(
-                            DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
-                    boolean fallback = bundle.getBoolean(
-                            DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
-                    HandoverParams handoverParams = mPendingHandovers.remove(msg);
-                    if (handoverParams != null) {
-                        onDataHandoverSetupCompleted(nr, success, transport, fallback,
-                                handoverParams);
-                    } else {
-                        logl("Handover completed but cannot find handover entry!");
-                    }
-                    break;
-                }
             }
         }
     }
 
     private int getTransportTypeFromNetworkRequest(TelephonyNetworkRequest networkRequest) {
-        if (PhoneFactory.getDefaultPhone().isUsingNewDataStack()) {
-            int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-            int capability = networkRequest.getApnTypeNetworkCapability();
-            if (capability >= 0) {
-                transport = mAccessNetworksManager
-                        .getPreferredTransportByNetworkCapability(capability);
-            }
-            return transport;
-        } else {
-            int apnType = ApnContext.getApnTypeFromNetworkRequest(
-                    networkRequest.getNativeNetworkRequest());
-            return mAccessNetworksManager.getCurrentTransport(apnType);
+        int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+        int capability = networkRequest.getApnTypeNetworkCapability();
+        if (capability >= 0) {
+            transport = mAccessNetworksManager
+                    .getPreferredTransportByNetworkCapability(capability);
         }
-    }
-
-    /**
-     * Request network
-     *
-     * @param networkRequest Network request from clients
-     * @param requestType The request type
-     * @param transport Transport type
-     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
-     * handover is completed. For normal request, this should be null.
-     */
-    private void requestNetworkInternal(TelephonyNetworkRequest networkRequest,
-            @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) {
-        NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
-                mSubscriptionId);
-
-        if (mPhone.isUsingNewDataStack()) {
-            mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
-        } else {
-            if (mPhone.getDcTracker(transport) != null) {
-                mPhone.getDcTracker(transport).requestNetwork(
-                        networkRequest.getNativeNetworkRequest(), requestType,
-                        onHandoverCompleteMsg);
-            }
-        }
-    }
-
-    private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest) {
-        mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
-    }
-
-    // TODO: Clean this up after old data stack removed.
-    private void releaseNetworkInternal(TelephonyNetworkRequest networkRequest,
-                                        @ReleaseNetworkType int releaseType,
-                                        int transport) {
-        if (mPhone.isUsingNewDataStack()) {
-            mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
-        } else {
-            if (mPhone.getDcTracker(transport) != null) {
-                mPhone.getDcTracker(transport).releaseNetwork(
-                        networkRequest.getNativeNetworkRequest(), releaseType);
-            }
-        }
+        return transport;
     }
 
     private static int getAction(boolean wasActive, boolean isActive) {
@@ -317,15 +223,11 @@
                     ? "Requesting" : "Releasing") + " network request " + networkRequest);
             int transportType = getTransportTypeFromNetworkRequest(networkRequest);
             if (action == ACTION_REQUEST) {
-                requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
-                        getTransportTypeFromNetworkRequest(networkRequest), null);
+                NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
+                        mSubscriptionId);
+                mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
             } else if (action == ACTION_RELEASE) {
-                if (mPhone.isUsingNewDataStack()) {
-                    releaseNetworkInternal(networkRequest);
-                } else {
-                    releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_DETACH,
-                            getTransportTypeFromNetworkRequest(networkRequest));
-                }
+                mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
             }
 
             mNetworkRequests.put(networkRequest,
@@ -365,8 +267,9 @@
         logl("onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply);
 
         if (shouldApply) {
-            requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
-                    getTransportTypeFromNetworkRequest(networkRequest), null);
+            NetworkRequestsStats.addNetworkRequest(networkRequest.getNativeNetworkRequest(),
+                    mSubscriptionId);
+            mPhone.getDataNetworkController().addNetworkRequest(networkRequest);
         }
     }
 
@@ -388,136 +291,10 @@
         logl("onReleaseNetworkFor " + networkRequest + " applied " + applied);
 
         if (applied) {
-            if (mPhone.isUsingNewDataStack()) {
-                releaseNetworkInternal(networkRequest);
-            } else {
-                // Most of the time, the network request only exists in one of the DcTracker, but in
-                // the middle of handover, the network request temporarily exists in both
-                // DcTrackers. If connectivity service releases the network request while handover
-                // is ongoing, we need to remove network requests from both DcTrackers.
-                // Note that this part will be refactored in T, where we won't even have DcTracker
-                // at all.
-                releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
-                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-                releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
-                        AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-            }
+            mPhone.getDataNetworkController().removeNetworkRequest(networkRequest);
         }
     }
 
-    private void onDataHandoverNeeded(@ApnType int apnType, int targetTransport,
-                                      HandoverParams handoverParams) {
-        log("onDataHandoverNeeded: apnType=" + ApnSetting.getApnTypeString(apnType)
-                + ", target transport="
-                + AccessNetworkConstants.transportTypeToString(targetTransport));
-        if (mAccessNetworksManager.getCurrentTransport(apnType) == targetTransport) {
-            log("APN type " + ApnSetting.getApnTypeString(apnType) + " is already on "
-                    + AccessNetworkConstants.transportTypeToString(targetTransport));
-            return;
-        }
-
-        boolean handoverPending = false;
-        for (Map.Entry<TelephonyNetworkRequest, Integer> entry : mNetworkRequests.entrySet()) {
-            TelephonyNetworkRequest networkRequest = entry.getKey();
-            int currentTransport = entry.getValue();
-            boolean applied = currentTransport != AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
-            if (ApnContext.getApnTypeFromNetworkRequest(
-                    networkRequest.getNativeNetworkRequest()) == apnType
-                    && applied
-                    && currentTransport != targetTransport) {
-                DcTracker dcTracker = mPhone.getDcTracker(currentTransport);
-                if (dcTracker != null) {
-                    DataConnection dc = dcTracker.getDataConnectionByApnType(
-                            ApnSetting.getApnTypeString(apnType));
-                    if (dc != null && (dc.isActive())) {
-                        Message onCompleteMsg = mInternalHandler.obtainMessage(
-                                EVENT_DATA_HANDOVER_COMPLETED);
-                        onCompleteMsg.getData().putParcelable(
-                                DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST,
-                                networkRequest.getNativeNetworkRequest());
-                        mPendingHandovers.put(onCompleteMsg, handoverParams);
-                        requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER,
-                                targetTransport, onCompleteMsg);
-                        log("Requested handover " + ApnSetting.getApnTypeString(apnType)
-                                + " to "
-                                + AccessNetworkConstants.transportTypeToString(targetTransport)
-                                + ". " + networkRequest);
-                        handoverPending = true;
-                    } else {
-                        // Request is there, but no actual data connection. In this case, just move
-                        // the request to the new transport.
-                        log("The network request is on transport " + AccessNetworkConstants
-                                .transportTypeToString(currentTransport) + ", but no live data "
-                                + "connection. Just move the request to transport "
-                                + AccessNetworkConstants.transportTypeToString(targetTransport)
-                                + ", dc=" + dc);
-                        entry.setValue(targetTransport);
-                        releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
-                                currentTransport);
-                        requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_NORMAL,
-                                targetTransport, null);
-                    }
-                } else {
-                    log("DcTracker on " + AccessNetworkConstants.transportTypeToString(
-                            currentTransport) + " is not available.");
-                }
-            }
-        }
-
-        if (!handoverPending) {
-            log("No handover request pending. Handover process is now completed");
-            handoverParams.callback.onCompleted(true, false);
-        }
-    }
-
-    private void onDataHandoverSetupCompleted(NetworkRequest request, boolean success,
-                                              int targetTransport, boolean fallback,
-                                              HandoverParams handoverParams) {
-        log("onDataHandoverSetupCompleted: " + request + ", success=" + success
-                + ", targetTransport="
-                + AccessNetworkConstants.transportTypeToString(targetTransport)
-                + ", fallback=" + fallback);
-
-        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(request, mPhone);
-        // At this point, handover setup has been completed on the target transport.
-        // If it succeeded, or it failed without falling back to the original transport,
-        // we should release the request from the original transport.
-        if (!fallback) {
-            int originTransport = DataUtils.getSourceTransport(targetTransport);
-            int releaseType = success
-                    ? DcTracker.RELEASE_TYPE_HANDOVER
-                    // If handover fails, we need to tear down the existing connection, so the
-                    // new data connection can be re-established on the new transport. If we leave
-                    // the existing data connection in current transport, then DCT and qualified
-                    // network service will be out of sync. Specifying release type to detach
-                    // the transport is moved to the other transport, but network request is still
-                    // there, connectivity service will not call unwanted to tear down the network.
-                    // We need explicitly tear down the data connection here so the new data
-                    // connection can be re-established on the other transport.
-                    : DcTracker.RELEASE_TYPE_DETACH;
-            releaseNetworkInternal(networkRequest, releaseType, originTransport);
-
-            // Before updating the network request with the target transport, make sure the request
-            // is still there because it's possible that connectivity service has already released
-            // the network while handover is ongoing. If connectivity service already released
-            // the network request, we need to tear down the just-handovered data connection on the
-            // target transport.
-            if (mNetworkRequests.containsKey(networkRequest)) {
-                // Update it with the target transport.
-                mNetworkRequests.put(networkRequest, targetTransport);
-            }
-        } else {
-            // If handover fails and requires to fallback, the context of target transport needs to
-            // be released
-            if (!success) {
-                releaseNetworkInternal(networkRequest,
-                        DcTracker.RELEASE_TYPE_NORMAL, targetTransport);
-            }
-        }
-
-        handoverParams.callback.onCompleted(success, fallback);
-    }
-
     protected void log(String s) {
         Rlog.d(LOG_TAG, s);
     }
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index b55304a..b334b89 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -157,8 +157,7 @@
     /**
      * Data config manager for retrieving data config.
      */
-    // TODO: Make this @NonNull after old data stack removed.
-    private final @Nullable DataConfigManager mDataConfigManager;
+    private final @NonNull DataConfigManager mDataConfigManager;
 
     /**
      * The attached data network. Note that the data network could be in any state. {@code null}
@@ -204,12 +203,8 @@
         // to satisfy it.
         mState = REQUEST_STATE_UNSATISFIED;
         mCreatedTimeMillis = SystemClock.elapsedRealtime();
-        if (phone.isUsingNewDataStack()) {
-            mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
-            updatePriority();
-        } else {
-            mDataConfigManager = null;
-        }
+        mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
+        updatePriority();
     }
 
     /**
@@ -401,8 +396,7 @@
      * @return {@code true} if this network request can result in bringing up a metered network.
      */
     public boolean isMeteredRequest() {
-        // TODO: Remove null check after old data stack removed.
-        return mDataConfigManager != null && mDataConfigManager.isAnyMeteredCapability(
+        return mDataConfigManager.isAnyMeteredCapability(
                 getCapabilities(), mPhone.getServiceState().getDataRoaming());
     }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java
deleted file mode 100644
index 827dbdd..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigType.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.telephony.Annotation;
-
-/**
- * Container of network configuration settings relevant for telephony module.
- *
- */
-public class ApnConfigType {
-
-    private final int mType;
-    private final int mPriority;
-
-    public ApnConfigType(@Annotation.ApnType int type, int priority) {
-        mType = type;
-        mPriority = priority;
-    }
-
-    /**
-     * Returns the apn type of this config type
-     * @return Type of apn.
-     */
-    public int getType() {
-        return mType;
-    }
-
-    /**
-     * Returns the priority of this apn config type.
-     * @return The priority of this apn.
-     */
-    public int getPriority() {
-        return mPriority;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
deleted file mode 100644
index 156ac92..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.PersistableBundle;
-import android.telephony.Annotation;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.data.ApnSetting;
-import android.util.ArrayMap;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Hard coded configuration of specific network types that the telephony module needs.
- * Formerly stored in network attributes within the resources file.
- */
-public class ApnConfigTypeRepository {
-
-    private static final String TAG = ApnConfigTypeRepository.class.getSimpleName();
-
-    private final Map<Integer, ApnConfigType> mConfigTypeMap;
-
-    public ApnConfigTypeRepository(PersistableBundle carrierConfig) {
-        mConfigTypeMap = new HashMap<>();
-        setup(carrierConfig);
-    }
-
-    /**
-     * Gets list of apn config types.
-     * @return All apn config types.
-     */
-    public Collection<ApnConfigType> getTypes() {
-        return mConfigTypeMap.values();
-    }
-
-    /**
-     * Gets the apn config type by apn type.
-     * @param type The ApnType to search for.
-     * @return The config type matching the given apn type.
-     */
-    @Nullable
-    public ApnConfigType getByType(@Annotation.ApnType int type) {
-        return mConfigTypeMap.get(type);
-    }
-
-    private void setup(PersistableBundle carrierConfig) {
-        addApns(getCarrierApnTypeMap(CarrierConfigManager.getDefaultConfig()));
-        addApns(getCarrierApnTypeMap(carrierConfig));
-    }
-
-    private void addApns(Map<Integer, Integer> apnTypeMap) {
-        add(ApnSetting.TYPE_DEFAULT, apnTypeMap);
-        add(ApnSetting.TYPE_MMS, apnTypeMap);
-        add(ApnSetting.TYPE_SUPL, apnTypeMap);
-        add(ApnSetting.TYPE_DUN, apnTypeMap);
-        add(ApnSetting.TYPE_HIPRI, apnTypeMap);
-        add(ApnSetting.TYPE_FOTA, apnTypeMap);
-        add(ApnSetting.TYPE_IMS, apnTypeMap);
-        add(ApnSetting.TYPE_CBS, apnTypeMap);
-        add(ApnSetting.TYPE_IA, apnTypeMap);
-        add(ApnSetting.TYPE_EMERGENCY, apnTypeMap);
-        add(ApnSetting.TYPE_MCX, apnTypeMap);
-        add(ApnSetting.TYPE_XCAP, apnTypeMap);
-        add(ApnSetting.TYPE_ENTERPRISE, apnTypeMap);
-    }
-
-    @NonNull
-    private Map<Integer, Integer> getCarrierApnTypeMap(PersistableBundle carrierConfig) {
-        if (carrierConfig == null) {
-            Rlog.w(TAG, "carrier config is null");
-            return new ArrayMap<>();
-        }
-
-        final String[] apnTypeConfig =
-                carrierConfig.getStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY);
-
-        final Map<Integer, Integer> apnTypeMap = new ArrayMap<>();
-        if (apnTypeConfig != null) {
-            for (final String entry : apnTypeConfig) {
-                try {
-                    final String[] keyValue = entry.split(":");
-                    if (keyValue.length != 2) {
-                        Rlog.e(TAG, "Apn type entry must have exactly one ':'");
-                    } else if (keyValue[0].contains(",")) {
-                        //getApnTypesBitmaskFromString parses commas to a list, not valid here.
-                        Rlog.e(TAG, "Invalid apn type name, entry: " + entry);
-                    } else {
-                        int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(keyValue[0]);
-                        if (apnTypeBitmask > 0) {
-                            apnTypeMap.put(apnTypeBitmask, Integer.parseInt(keyValue[1]));
-                        } else {
-                            Rlog.e(TAG, "Invalid apn type name, entry: " + entry);
-                        }
-                    }
-
-                } catch (Exception ex) {
-                    Rlog.e(TAG, "Exception on apn type entry: " + entry + "\n", ex);
-                }
-            }
-        }
-        return apnTypeMap;
-    }
-
-    private void add(@Annotation.ApnType int type, Map<Integer, Integer> apnTypeMap) {
-        if (apnTypeMap.containsKey(type)) {
-            mConfigTypeMap.put(type, new ApnConfigType(type, apnTypeMap.get(type)));
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
deleted file mode 100644
index 3f487cd..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ /dev/null
@@ -1,675 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.os.Message;
-import android.telephony.Annotation.ApnType;
-import android.telephony.data.ApnSetting;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.LocalLog;
-import android.util.SparseIntArray;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Maintain the Apn context
- */
-public class ApnContext {
-
-    public final String LOG_TAG;
-    private final static String SLOG_TAG = "ApnContext";
-
-    protected static final boolean DBG = false;
-
-    private final Phone mPhone;
-
-    private final String mApnType;
-
-    private DctConstants.State mState;
-
-    private int mPriority;
-
-    private ApnSetting mApnSetting;
-
-    private DataConnection mDataConnection;
-
-    private String mReason;
-
-    /**
-     * user/app requested connection on this APN
-     */
-    AtomicBoolean mDataEnabled;
-
-    private final Object mRefCountLock = new Object();
-
-    private final DcTracker mDcTracker;
-
-
-    /**
-     * Remember this as a change in this value to a more permissive state
-     * should cause us to retry even permanent failures
-     */
-    private boolean mConcurrentVoiceAndDataAllowed;
-
-    /**
-     * used to track a single connection request so disconnects can get ignored if
-     * obsolete.
-     */
-    private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
-
-    /**
-     * Retry manager that handles the APN retry and delays.
-     */
-    private final RetryManager mRetryManager;
-
-    /**
-     * ApnContext constructor
-     * @param phone phone object
-     * @param typeId APN type Id
-     * @param logTag Tag for logging
-     * @param tracker Data call tracker
-     * @param priority Priority of APN type
-     */
-    public ApnContext(Phone phone, int typeId, String logTag, DcTracker tracker, int priority) {
-        this(phone, ApnSetting.getApnTypeString(typeId), logTag, tracker, priority);
-    }
-
-    /**
-     * ApnContext constructor
-     * @param phone phone object
-     * @param apnType APN type (e.g. default, supl, mms, etc...)
-     * @param logTag Tag for logging
-     * @param tracker Data call tracker
-     * @param priority Priority of APN type
-     */
-    public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority) {
-        mPhone = phone;
-        mApnType = apnType;
-        mState = DctConstants.State.IDLE;
-        setReason(Phone.REASON_DATA_ENABLED);
-        mDataEnabled = new AtomicBoolean(false);
-        mPriority = priority;
-        LOG_TAG = logTag;
-        mDcTracker = tracker;
-        mRetryManager = new RetryManager(phone, tracker.getDataThrottler(),
-                ApnSetting.getApnTypesBitmaskFromString(apnType));
-    }
-
-
-
-    /**
-     * Get the APN type
-     * @return The APN type
-     */
-    public String getApnType() {
-        return mApnType;
-    }
-
-    /**
-     * Gets the APN type bitmask.
-     * @return The APN type bitmask
-     */
-    public int getApnTypeBitmask() {
-        return ApnSetting.getApnTypesBitmaskFromString(mApnType);
-    }
-
-    /**
-     * Get the associated data connection
-     * @return The data connection
-     */
-    public synchronized DataConnection getDataConnection() {
-        return mDataConnection;
-    }
-
-    /**
-     * This priority is taken into account when concurrent data connections are not allowed.  The
-     * APN with the HIGHER priority is given preference.
-     * @return The priority of the APN type
-     */
-    public int getPriority() {
-        return mPriority;
-    }
-
-    /**
-     * Updates the priority of this context.
-     * @param priority The priority of the APN type
-     */
-    public void setPriority(int priority) {
-        mPriority = priority;
-    }
-
-    /**
-     * Keeping for backwards compatibility and in case it's needed in the future
-     * @return true
-     */
-    public boolean isDependencyMet() {
-        return true;
-    }
-
-    /**
-     * Set the associated data connection.
-     * @param dc data connection
-     */
-    public synchronized void setDataConnection(DataConnection dc) {
-        log("setDataConnectionAc: old=" + mDataConnection + ",new=" + dc + " this=" + this);
-        mDataConnection = dc;
-    }
-
-    /**
-     * Release data connection.
-     * @param reason The reason of releasing data connection
-     */
-    public synchronized void releaseDataConnection(String reason) {
-        if (mDataConnection != null) {
-            mDataConnection.tearDown(this, reason, null);
-            mDataConnection = null;
-        }
-        setState(DctConstants.State.IDLE);
-    }
-
-    /**
-     * Get the current APN setting.
-     * @return APN setting
-     */
-    public synchronized ApnSetting getApnSetting() {
-        log("getApnSetting: apnSetting=" + mApnSetting);
-        return mApnSetting;
-    }
-
-    /**
-     * Set the APN setting.
-     * @param apnSetting APN setting
-     */
-    public synchronized void setApnSetting(ApnSetting apnSetting) {
-        log("setApnSetting: apnSetting=" + apnSetting);
-        mApnSetting = apnSetting;
-    }
-
-    /**
-     * Set the list of APN candidates which will be used for data call setup later.
-     * @param waitingApns List of APN candidates
-     */
-    public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
-        mRetryManager.setWaitingApns(waitingApns);
-    }
-
-    /**
-     * Get the next available APN to try.
-     * @return APN setting which will be used for data call setup.{@code null} if there is no
-     * APN can be retried.
-     */
-    public @Nullable ApnSetting getNextApnSetting() {
-        return mRetryManager.getNextApnSetting();
-    }
-
-    /**
-     * Get the delay for trying the next APN setting if the current one failed.
-     * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
-     * delay.
-     * @return The delay in milliseconds
-     */
-    public long getDelayForNextApn(boolean failFastEnabled) {
-        return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason());
-    }
-
-    /**
-     * Mark the current APN setting permanently failed, which means it will not be retried anymore.
-     * @param apn APN setting
-     */
-    public void markApnPermanentFailed(ApnSetting apn) {
-        mRetryManager.markApnPermanentFailed(apn);
-    }
-
-    /**
-     * Get the list of waiting APNs.
-     * @return the list of waiting APNs
-     */
-    public @NonNull ArrayList<ApnSetting> getWaitingApns() {
-        return mRetryManager.getWaitingApns();
-    }
-
-    /**
-     * Save the state indicating concurrent voice/data allowed.
-     * @param allowed True if concurrent voice/data is allowed
-     */
-    public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
-        mConcurrentVoiceAndDataAllowed = allowed;
-    }
-
-    /**
-     * Get the state indicating concurrent voice/data allowed.
-     * @return True if concurrent voice/data is allowed
-     */
-    public synchronized boolean isConcurrentVoiceAndDataAllowed() {
-        return mConcurrentVoiceAndDataAllowed;
-    }
-
-    /**
-     * Set the current data call state.
-     * @param s Current data call state
-     */
-    public synchronized void setState(DctConstants.State s) {
-        log("setState: " + s + ", previous state:" + mState);
-
-        if (mState != s) {
-            mStateLocalLog.log("State changed from " + mState + " to " + s);
-            mState = s;
-        }
-
-        if (mState == DctConstants.State.FAILED) {
-            // when teardown the connection and set to IDLE
-            mRetryManager.getWaitingApns().clear();
-        }
-    }
-
-    /**
-     * Get the current data call state.
-     * @return The current data call state
-     */
-    public synchronized DctConstants.State getState() {
-        return mState;
-    }
-
-    /**
-     * Check whether the data call is disconnected or not.
-     * @return True if the data call is disconnected
-     */
-    public boolean isDisconnected() {
-        DctConstants.State currentState = getState();
-        return ((currentState == DctConstants.State.IDLE) ||
-                    currentState == DctConstants.State.FAILED);
-    }
-
-    /**
-     * Set the reason for data call connection.
-     * @param reason Reason for data call connection
-     */
-    public synchronized void setReason(String reason) {
-        log("set reason as " + reason + ",current state " + mState);
-        mReason = reason;
-    }
-
-    /**
-     * Get the reason for data call connection.
-     * @return The reason for data call connection
-     */
-    public synchronized String getReason() {
-        return mReason;
-    }
-
-    /**
-     * Check if ready for data call connection
-     * @return True if ready, otherwise false.
-     */
-    public boolean isReady() {
-        return mDataEnabled.get() && isDependencyMet();
-    }
-
-    /**
-     * Check if the data call is in the state which allow connecting.
-     * @return True if allowed, otherwise false.
-     */
-    public boolean isConnectable() {
-        return isReady() && ((mState == DctConstants.State.IDLE)
-                                || (mState == DctConstants.State.RETRYING)
-                                || (mState == DctConstants.State.FAILED));
-    }
-
-    /**
-     * Check if apn reason is fast retry reason which should apply shorter delay between apn re-try.
-     * @return True if it is fast retry reason, otherwise false.
-     */
-    private boolean isFastRetryReason() {
-        return Phone.REASON_NW_TYPE_CHANGED.equals(mReason) ||
-                Phone.REASON_APN_CHANGED.equals(mReason);
-    }
-
-    /** Check if the data call is in connected or connecting state.
-     * @return True if the data call is in connected or connecting state
-     */
-    public boolean isConnectedOrConnecting() {
-        return isReady() && ((mState == DctConstants.State.CONNECTED)
-                                || (mState == DctConstants.State.CONNECTING)
-                                || (mState == DctConstants.State.RETRYING));
-    }
-
-    /**
-     * Set data call enabled/disabled state.
-     * @param enabled True if data call is enabled
-     */
-    public void setEnabled(boolean enabled) {
-        log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
-        mDataEnabled.set(enabled);
-    }
-
-    /**
-     * Check if the data call is enabled or not.
-     * @return True if enabled
-     */
-    public boolean isEnabled() {
-        return mDataEnabled.get();
-    }
-
-    public boolean isProvisioningApn() {
-        String provisioningApn = mPhone.getContext().getResources()
-                .getString(R.string.mobile_provisioning_apn);
-        if (!TextUtils.isEmpty(provisioningApn) &&
-                (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
-            return (mApnSetting.getApnName().equals(provisioningApn));
-        } else {
-            return false;
-        }
-    }
-
-    private final ArraySet<NetworkRequest> mNetworkRequests = new ArraySet<>();
-    private final LocalLog mStateLocalLog = new LocalLog(32);
-
-    private static final LocalLog sLocalLog = new LocalLog(256);
-
-    /** Add a line to the ApnContext local log. */
-    public static void requestLog(ApnContext apnContext, String str) {
-        if (apnContext != null) {
-            String logString = "[ApnContext:" + apnContext.getApnType() + "] " + str;
-            if (DBG) {
-                Rlog.d(SLOG_TAG, logString);
-            }
-            synchronized (sLocalLog) {
-                sLocalLog.log(logString);
-            }
-        }
-    }
-
-    /**
-     * Request a network
-     *
-     * @param networkRequest Network request from clients
-     * @param type The request type
-     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
-     * handover is completed. For normal request, this should be null.
-     */
-    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
-            Message onHandoverCompleteMsg) {
-        synchronized (mRefCountLock) {
-            mNetworkRequests.add(networkRequest);
-            requestLog(this, "requestNetwork for " + networkRequest + ", type="
-                    + DcTracker.requestTypeToString(type));
-            mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
-                    onHandoverCompleteMsg);
-            if (mDataConnection != null) {
-                // New network request added. Should re-evaluate properties of
-                // the data connection. For example, the score may change.
-                mDataConnection.reevaluateDataConnectionProperties();
-            }
-        }
-    }
-
-    public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
-        synchronized (mRefCountLock) {
-            if (mNetworkRequests.contains(networkRequest)) {
-                mNetworkRequests.remove(networkRequest);
-                if (mDataConnection != null) {
-                    // New network request added. Should re-evaluate properties of
-                    // the data connection. For example, the score may change.
-                    mDataConnection.reevaluateDataConnectionProperties();
-                }
-                requestLog(this, "releaseNetwork left with " + mNetworkRequests.size()
-                        + " requests.");
-                if (mNetworkRequests.size() == 0
-                        || type == DcTracker.RELEASE_TYPE_DETACH
-                        || type == DcTracker.RELEASE_TYPE_HANDOVER) {
-                    mDcTracker.disableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type);
-                }
-            }
-        }
-    }
-
-    /**
-     * @param excludeDun True if excluding requests that have DUN capability
-     * @return True if the attached network requests contain restricted capability.
-     */
-    public boolean hasRestrictedRequests(boolean excludeDun) {
-        synchronized (mRefCountLock) {
-            for (NetworkRequest nr : mNetworkRequests) {
-                if (excludeDun &&
-                        nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-                    continue;
-                }
-                if (!nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
-
-    public void resetErrorCodeRetries() {
-        requestLog(this, "resetErrorCodeRetries");
-
-        String[] config = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_cell_retries_per_error_code);
-        synchronized (mRetriesLeftPerErrorCode) {
-            mRetriesLeftPerErrorCode.clear();
-
-            for (String c : config) {
-                String errorValue[] = c.split(",");
-                if (errorValue != null && errorValue.length == 2) {
-                    int count = 0;
-                    int errorCode = 0;
-                    try {
-                        errorCode = Integer.parseInt(errorValue[0]);
-                        count = Integer.parseInt(errorValue[1]);
-                    } catch (NumberFormatException e) {
-                        log("Exception parsing config_retries_per_error_code: " + e);
-                        continue;
-                    }
-                    if (count > 0 && errorCode > 0) {
-                        mRetriesLeftPerErrorCode.put(errorCode, count);
-                    }
-                } else {
-                    log("Exception parsing config_retries_per_error_code: " + c);
-                }
-            }
-        }
-    }
-
-    public boolean restartOnError(int errorCode) {
-        boolean result = false;
-        int retriesLeft = 0;
-        synchronized(mRetriesLeftPerErrorCode) {
-            retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
-            switch (retriesLeft) {
-                case 0: {
-                    // not set, never restart modem
-                    break;
-                }
-                case 1: {
-                    resetErrorCodeRetries();
-                    result = true;
-                    break;
-                }
-                default: {
-                    mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
-                    result = false;
-                }
-            }
-        }
-        requestLog(this, "restartOnError(" + errorCode + ") found " + retriesLeft
-                + " and returned " + result);
-        return result;
-    }
-
-    public int incAndGetConnectionGeneration() {
-        return mConnectionGeneration.incrementAndGet();
-    }
-
-    public int getConnectionGeneration() {
-        return mConnectionGeneration.get();
-    }
-
-    long getRetryAfterDisconnectDelay() {
-        return mRetryManager.getRetryAfterDisconnectDelay();
-    }
-
-    /**
-     * Get APN type from the network request.
-     *
-     * @param nr The network request.
-     * @return The APN type.
-     */
-    public static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
-        // For now, ignore the bandwidth stuff
-        if (nr.getTransportTypes().length > 0
-                && !nr.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
-            return ApnSetting.TYPE_NONE;
-        }
-
-        // in the near term just do 1-1 matches.
-        // TODO - actually try to match the set of capabilities
-        int apnType = ApnSetting.TYPE_NONE;
-        boolean error = false;
-
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            apnType = ApnSetting.TYPE_DEFAULT;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_MMS;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_SUPL;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_DUN;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_FOTA;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_IMS;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_CBS;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_IA;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_EMERGENCY;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_MCX;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_XCAP;
-        }
-        if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) {
-            if (apnType != ApnSetting.TYPE_NONE) error = true;
-            apnType = ApnSetting.TYPE_ENTERPRISE;
-        }
-        if (error) {
-            // TODO: If this error condition is removed, the framework's handling of
-            // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
-            // say FOTA and INTERNET are marked as restricted.  This is not how
-            // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
-            Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
-        }
-        if (apnType == ApnSetting.TYPE_NONE) {
-            Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
-        }
-        return apnType;
-    }
-
-    public List<NetworkRequest> getNetworkRequests() {
-        synchronized (mRefCountLock) {
-            return new ArrayList<NetworkRequest>(mNetworkRequests);
-        }
-    }
-
-    @Override
-    public synchronized String toString() {
-        // We don't print mDataConnection because its recursive.
-        return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={"
-                    + mRetryManager.getWaitingApns() + " priority=" + mPriority + "}"
-                    + " mApnSetting={" + mApnSetting
-                    + "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + "}";
-    }
-
-    private void log(String s) {
-        if (DBG) {
-            Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
-        }
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
-        final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
-        synchronized (mRefCountLock) {
-            pw.println(toString());
-            if (mNetworkRequests.size() > 0) {
-                pw.println("NetworkRequests:");
-                pw.increaseIndent();
-                for (NetworkRequest nr : mNetworkRequests) {
-                    pw.println(nr);
-                }
-                pw.decreaseIndent();
-            }
-            pw.println("Historical APN state:");
-            pw.increaseIndent();
-            mStateLocalLog.dump(fd, pw, args);
-            pw.decreaseIndent();
-            pw.println(mRetryManager);
-            pw.println("--------------------------");
-        }
-    }
-
-    /** Dumps the ApnContext local log. */
-    public static void dumpLocalLog(FileDescriptor fd, PrintWriter printWriter, String[] args) {
-        printWriter.println("Local log:");
-        synchronized (sLocalLog) {
-            sLocalLog.dump(fd, printWriter, args);
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
deleted file mode 100644
index 3c2a6ef..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-
-import com.android.internal.telephony.Phone;
-import com.android.telephony.Rlog;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-/**
- * This class represents a apn setting for create PDP link
- */
-public class ApnSettingUtils {
-
-    static final String LOG_TAG = "ApnSetting";
-
-    private static final boolean DBG = false;
-
-    /**
-     * Check if this APN type is metered.
-     *
-     * @param apnType the APN type
-     * @param phone the phone object
-     * @return {@code true} if the APN type is metered, {@code false} otherwise.
-     */
-    public static boolean isMeteredApnType(@ApnType int apnType, Phone phone) {
-        if (phone == null) {
-            return true;
-        }
-
-        boolean isRoaming = phone.getServiceState().getDataRoaming();
-        int subId = phone.getSubId();
-
-        String carrierConfig;
-        // First check if the device is roaming. If yes, use the roaming metered APN list.
-        // Otherwise use the normal metered APN list.
-        if (isRoaming) {
-            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
-        } else {
-            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
-        }
-
-        if (DBG) {
-            Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming);
-        }
-
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager == null) {
-            Rlog.e(LOG_TAG, "Carrier config service is not available");
-            return true;
-        }
-
-        PersistableBundle b = configManager.getConfigForSubId(subId);
-        if (b == null) {
-            Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
-            return true;
-        }
-
-        String[] meteredApnTypes = b.getStringArray(carrierConfig);
-        if (meteredApnTypes == null) {
-            Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
-            return true;
-        }
-
-        HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
-        if (DBG) {
-            Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
-                    + Arrays.toString(meteredApnSet.toArray()));
-        }
-
-        if (meteredApnSet.contains(ApnSetting.getApnTypeString(apnType))) {
-            if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is metered.");
-            return true;
-        } else if (apnType == ApnSetting.TYPE_ALL) {
-            // Assuming no configuration error, if at least one APN type is
-            // metered, then this APN setting is metered.
-            if (meteredApnSet.size() > 0) {
-                if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
-                return true;
-            }
-        }
-
-        if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is not metered.");
-        return false;
-    }
-
-    /**
-     * Check if this APN setting is metered.
-     *
-     * @param apn APN setting
-     * @param phone The phone object
-     * @return True if this APN setting is metered, otherwise false.
-     */
-    public static boolean isMetered(ApnSetting apn, Phone phone) {
-        if (phone == null || apn == null) {
-            return true;
-        }
-
-        for (int apnType : apn.getApnTypes()) {
-            // If one of the APN type is metered, then this APN setting is metered.
-            if (isMeteredApnType(apnType, phone)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
deleted file mode 100644
index 255972a..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ /dev/null
@@ -1,4094 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET;
-
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.net.ConnectivityManager;
-import android.net.InetAddresses;
-import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
-import android.net.NetworkProvider;
-import android.net.NetworkRequest;
-import android.net.ProxyInfo;
-import android.net.RouteInfo;
-import android.net.SocketKeepalive;
-import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnManager;
-import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
-import android.net.vcn.VcnNetworkPolicyResult;
-import android.os.AsyncResult;
-import android.os.HandlerExecutor;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserManager;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.Annotation.DataState;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.LinkCapacityEstimate;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataCallResponse.HandoverFailureMode;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.NetworkSliceInfo;
-import android.telephony.data.Qos;
-import android.telephony.data.QosBearerSession;
-import android.telephony.data.TrafficDescriptor;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Pair;
-import android.util.TimeUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.CarrierPrivilegesTracker;
-import com.android.internal.telephony.CarrierSignalAgent;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.RIL;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyStatsLog;
-import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
-import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
-import com.android.internal.telephony.metrics.DataCallSessionStats;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-/**
- * {@hide}
- *
- * DataConnection StateMachine.
- *
- * This a class for representing a single data connection, with instances of this
- * class representing a connection via the cellular network. There may be multiple
- * data connections and all of them are managed by the <code>DataConnectionTracker</code>.
- *
- * NOTE: All DataConnection objects must be running on the same looper, which is the default
- * as the coordinator has members which are used without synchronization.
- */
-public class DataConnection extends StateMachine {
-    private static final boolean DBG = true;
-    private static final boolean VDBG = true;
-
-    private static final String NETWORK_TYPE = "MOBILE";
-
-    private static final String RAT_NAME_5G = "nr";
-    private static final String RAT_NAME_EVDO = "evdo";
-
-    /**
-     * OSId for "Android", using UUID version 5 with namespace ISO OSI.
-     * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
-     */
-    private static final UUID OS_ID = UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
-
-    /**
-     * The data connection is not being or been handovered. Note this is the state for the source
-     * data connection, not destination data connection
-     */
-    private static final int HANDOVER_STATE_IDLE = 1;
-
-    /**
-     * The data connection is being handovered. Note this is the state for the source
-     * data connection, not destination data connection.
-     */
-    private static final int HANDOVER_STATE_BEING_TRANSFERRED = 2;
-
-    /**
-     * The data connection is already handovered. Note this is the state for the source
-     * data connection, not destination data connection.
-     */
-    private static final int HANDOVER_STATE_COMPLETED = 3;
-
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"HANDOVER_STATE_"}, value = {
-            HANDOVER_STATE_IDLE,
-            HANDOVER_STATE_BEING_TRANSFERRED,
-            HANDOVER_STATE_COMPLETED})
-    public @interface HandoverState {}
-
-    // The data connection providing default Internet connection will have a higher score of 50.
-    // Other connections will have a slightly lower score of 45. The intention is other connections
-    // will not cause ConnectivityService to tear down default internet connection. For example,
-    // to validate Internet connection on non-default data SIM, we'll set up a temporary Internet
-    // connection on that data SIM. In this case, score of 45 is assigned so ConnectivityService
-    // will not replace the default Internet connection with it.
-    private static final int DEFAULT_INTERNET_CONNECTION_SCORE = 50;
-    private static final int OTHER_CONNECTION_SCORE = 45;
-
-    // The score we report to connectivity service
-    private int mScore;
-
-    // The subscription id associated with this data connection.
-    private int mSubId;
-
-    // The data connection controller
-    private DcController mDcController;
-
-    // The Tester for failing all bringup's
-    private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
-
-    // Whether or not the data connection should allocate its own pdu session id
-    private boolean mDoAllocatePduSessionId;
-
-    private static AtomicInteger mInstanceNumber = new AtomicInteger(0);
-    private AsyncChannel mAc;
-
-    // The DCT that's talking to us, we only support one!
-    private DcTracker mDct = null;
-
-    private String[] mPcscfAddr;
-
-    private final String mTagSuffix;
-
-    private final LocalLog mHandoverLocalLog = new LocalLog(64);
-
-    private int[] mAdministratorUids = new int[0];
-
-    // stats per data call
-    private DataCallSessionStats mDataCallSessionStats;
-
-    /**
-     * Used internally for saving connecting parameters.
-     */
-    public static class ConnectionParams {
-        int mTag;
-        ApnContext mApnContext;
-        int mProfileId;
-        int mRilRat;
-        Message mOnCompletedMsg;
-        final int mConnectionGeneration;
-        @RequestNetworkType
-        final int mRequestType;
-        final int mSubId;
-        final boolean mIsPreferredApn;
-
-        ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology,
-                         Message onCompletedMsg, int connectionGeneration,
-                         @RequestNetworkType int requestType, int subId,
-                         boolean isPreferredApn) {
-            mApnContext = apnContext;
-            mProfileId = profileId;
-            mRilRat = rilRadioTechnology;
-            mOnCompletedMsg = onCompletedMsg;
-            mConnectionGeneration = connectionGeneration;
-            mRequestType = requestType;
-            mSubId = subId;
-            mIsPreferredApn = isPreferredApn;
-        }
-
-        @Override
-        public String toString() {
-            return "{mTag=" + mTag + " mApnContext=" + mApnContext
-                    + " mProfileId=" + mProfileId
-                    + " mRat=" + mRilRat
-                    + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg)
-                    + " mRequestType=" + DcTracker.requestTypeToString(mRequestType)
-                    + " mSubId=" + mSubId
-                    + " mIsPreferredApn=" + mIsPreferredApn
-                    + "}";
-        }
-    }
-
-    /**
-     * Used internally for saving disconnecting parameters.
-     */
-    public static class DisconnectParams {
-        int mTag;
-        public ApnContext mApnContext;
-        String mReason;
-        @ReleaseNetworkType
-        final int mReleaseType;
-        Message mOnCompletedMsg;
-
-        DisconnectParams(ApnContext apnContext, String reason, @ReleaseNetworkType int releaseType,
-                         Message onCompletedMsg) {
-            mApnContext = apnContext;
-            mReason = reason;
-            mReleaseType = releaseType;
-            mOnCompletedMsg = onCompletedMsg;
-        }
-
-        @Override
-        public String toString() {
-            return "{mTag=" + mTag + " mApnContext=" + mApnContext
-                    + " mReason=" + mReason
-                    + " mReleaseType=" + DcTracker.releaseTypeToString(mReleaseType)
-                    + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
-        }
-    }
-
-    private volatile ApnSetting mApnSetting;
-    private ConnectionParams mConnectionParams;
-    private DisconnectParams mDisconnectParams;
-    @DataFailureCause
-    private int mDcFailCause;
-
-    @HandoverFailureMode
-    private int mHandoverFailureMode;
-
-    private Phone mPhone;
-    private DataServiceManager mDataServiceManager;
-    private VcnManager mVcnManager;
-    private final int mTransportType;
-    private LinkProperties mLinkProperties = new LinkProperties();
-    private int mPduSessionId;
-    private long mCreateTime;
-    private long mLastFailTime;
-    @DataFailureCause
-    private int mLastFailCause;
-    private static final String NULL_IP = "0.0.0.0";
-    private Object mUserData;
-    private boolean mCongestedOverride;
-    private boolean mUnmeteredOverride;
-    private int mRilRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-    private int mDataRegState = Integer.MAX_VALUE;
-    // Indicating data connection is suspended due to temporary reasons, for example, out of
-    // service, concurrency voice/data not supported, etc.. Note this flag is only meaningful when
-    // data is in active state. When data is in inactive, connecting, or disconnecting, this flag
-    // is unmeaningful.
-    private boolean mIsSuspended;
-    private int mDownlinkBandwidth = 14;
-    private int mUplinkBandwidth = 14;
-    private Qos mDefaultQos = null;
-    private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
-    private NetworkSliceInfo mSliceInfo;
-    private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
-
-    /** The corresponding network agent for this data connection. */
-    private DcNetworkAgent mNetworkAgent;
-
-    /**
-     * The network agent from handover source data connection. This is the potential network agent
-     * that will be transferred here after handover completed.
-     */
-    private DcNetworkAgent mHandoverSourceNetworkAgent;
-
-    private int mDisabledApnTypeBitMask = 0;
-
-    int mTag;
-
-    /** Data connection id assigned by the modem. This is unique across transports */
-    public int mCid;
-
-    @HandoverState
-    private int mHandoverState = HANDOVER_STATE_IDLE;
-    private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>();
-    PendingIntent mReconnectIntent = null;
-
-    /** Class used to track VCN-defined Network policies for this DcNetworkAgent. */
-    private final VcnNetworkPolicyChangeListener mVcnPolicyChangeListener =
-            new DataConnectionVcnNetworkPolicyChangeListener();
-
-    // ***** Event codes for driving the state machine, package visible for Dcc
-    static final int BASE = Protocol.BASE_DATA_CONNECTION;
-    static final int EVENT_CONNECT = BASE + 0;
-    static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
-    static final int EVENT_DEACTIVATE_DONE = BASE + 3;
-    static final int EVENT_DISCONNECT = BASE + 4;
-    static final int EVENT_DISCONNECT_ALL = BASE + 6;
-    static final int EVENT_DATA_STATE_CHANGED = BASE + 7;
-    static final int EVENT_TEAR_DOWN_NOW = BASE + 8;
-    static final int EVENT_LOST_CONNECTION = BASE + 9;
-    static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11;
-    static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12;
-    static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13;
-    static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14;
-    static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15;
-    static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16;
-    static final int EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED = BASE + 17;
-    static final int EVENT_KEEPALIVE_STATUS = BASE + 18;
-    static final int EVENT_KEEPALIVE_STARTED = BASE + 19;
-    static final int EVENT_KEEPALIVE_STOPPED = BASE + 20;
-    static final int EVENT_KEEPALIVE_START_REQUEST = BASE + 21;
-    static final int EVENT_KEEPALIVE_STOP_REQUEST = BASE + 22;
-    static final int EVENT_LINK_CAPACITY_CHANGED = BASE + 23;
-    static final int EVENT_RESET = BASE + 24;
-    static final int EVENT_REEVALUATE_RESTRICTED_STATE = BASE + 25;
-    static final int EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES = BASE + 26;
-    static final int EVENT_NR_STATE_CHANGED = BASE + 27;
-    static final int EVENT_DATA_CONNECTION_METEREDNESS_CHANGED = BASE + 28;
-    static final int EVENT_NR_FREQUENCY_CHANGED = BASE + 29;
-    static final int EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED = BASE + 30;
-    static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = BASE + 31;
-    static final int EVENT_CSS_INDICATOR_CHANGED = BASE + 32;
-    static final int EVENT_UPDATE_SUSPENDED_STATE = BASE + 33;
-    static final int EVENT_START_HANDOVER = BASE + 34;
-    static final int EVENT_CANCEL_HANDOVER = BASE + 35;
-    static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36;
-    static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37;
-    static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38;
-    static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39;
-    private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1;
-
-    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
-    static {
-        sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
-        sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
-                "EVENT_SETUP_DATA_CONNECTION_DONE";
-        sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
-        sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
-        sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
-        sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED";
-        sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW";
-        sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION";
-        sCmdToString[EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED - BASE] =
-                "EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED";
-        sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON";
-        sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF";
-        sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE";
-        sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] =
-                "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
-        sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
-                "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
-        sCmdToString[EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED - BASE] =
-                "EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED";
-        sCmdToString[EVENT_KEEPALIVE_STATUS - BASE] = "EVENT_KEEPALIVE_STATUS";
-        sCmdToString[EVENT_KEEPALIVE_STARTED - BASE] = "EVENT_KEEPALIVE_STARTED";
-        sCmdToString[EVENT_KEEPALIVE_STOPPED - BASE] = "EVENT_KEEPALIVE_STOPPED";
-        sCmdToString[EVENT_KEEPALIVE_START_REQUEST - BASE] = "EVENT_KEEPALIVE_START_REQUEST";
-        sCmdToString[EVENT_KEEPALIVE_STOP_REQUEST - BASE] = "EVENT_KEEPALIVE_STOP_REQUEST";
-        sCmdToString[EVENT_LINK_CAPACITY_CHANGED - BASE] = "EVENT_LINK_CAPACITY_CHANGED";
-        sCmdToString[EVENT_RESET - BASE] = "EVENT_RESET";
-        sCmdToString[EVENT_REEVALUATE_RESTRICTED_STATE - BASE] =
-                "EVENT_REEVALUATE_RESTRICTED_STATE";
-        sCmdToString[EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES - BASE] =
-                "EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES";
-        sCmdToString[EVENT_NR_STATE_CHANGED - BASE] = "EVENT_NR_STATE_CHANGED";
-        sCmdToString[EVENT_DATA_CONNECTION_METEREDNESS_CHANGED - BASE] =
-                "EVENT_DATA_CONNECTION_METEREDNESS_CHANGED";
-        sCmdToString[EVENT_NR_FREQUENCY_CHANGED - BASE] = "EVENT_NR_FREQUENCY_CHANGED";
-        sCmdToString[EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED - BASE] =
-                "EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED";
-        sCmdToString[EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE] =
-                "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
-        sCmdToString[EVENT_CSS_INDICATOR_CHANGED - BASE] = "EVENT_CSS_INDICATOR_CHANGED";
-        sCmdToString[EVENT_UPDATE_SUSPENDED_STATE - BASE] = "EVENT_UPDATE_SUSPENDED_STATE";
-        sCmdToString[EVENT_START_HANDOVER - BASE] = "EVENT_START_HANDOVER";
-        sCmdToString[EVENT_CANCEL_HANDOVER - BASE] = "EVENT_CANCEL_HANDOVER";
-        sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET";
-        sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID";
-        sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID";
-        sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] =
-                "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE";
-    }
-    // Convert cmd to string or null if unknown
-    static String cmdToString(int cmd) {
-        String value = null;
-        cmd -= BASE;
-        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
-            value = sCmdToString[cmd];
-        }
-        if (value == null) {
-            value = "0x" + Integer.toHexString(cmd + BASE);
-        }
-        return value;
-    }
-
-    /**
-     * Create the connection object
-     *
-     * @param phone the Phone
-     * @param id the connection id
-     * @return DataConnection that was created.
-     */
-    public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct,
-                                                    DataServiceManager dataServiceManager,
-                                                    DcTesterFailBringUpAll failBringUpAll,
-                                                    DcController dcc) {
-        String transportType = (dataServiceManager.getTransportType()
-                == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                ? "C"   // Cellular
-                : "I";  // IWLAN
-        DataConnection dc = new DataConnection(phone, transportType + "-"
-                + mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager, failBringUpAll,
-                dcc);
-        dc.start();
-        if (DBG) dc.log("Made " + dc.getName());
-        return dc;
-    }
-
-    void dispose() {
-        log("dispose: call quiteNow()");
-        quitNow();
-    }
-
-    /* Getter functions */
-
-    LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    boolean isDisconnecting() {
-        return getCurrentState() == mDisconnectingState
-                || getCurrentState() == mDisconnectingErrorCreatingConnection;
-    }
-
-    @VisibleForTesting
-    public boolean isActive() {
-        return getCurrentState() == mActiveState;
-    }
-
-    @VisibleForTesting
-    public boolean isInactive() {
-        return getCurrentState() == mInactiveState;
-    }
-
-    boolean isActivating() {
-        return getCurrentState() == mActivatingState;
-    }
-
-    boolean hasBeenTransferred() {
-        return mHandoverState == HANDOVER_STATE_COMPLETED;
-    }
-
-    int getCid() {
-        return mCid;
-    }
-
-    /**
-     * @return DataConnection's ApnSetting.
-     */
-    public ApnSetting getApnSetting() {
-        return mApnSetting;
-    }
-
-    /**
-     * Update http proxy of link properties based on current apn setting
-     */
-    private void updateLinkPropertiesHttpProxy() {
-        if (mApnSetting == null
-                || TextUtils.isEmpty(mApnSetting.getProxyAddressAsString())) {
-            return;
-        }
-        try {
-            int port = mApnSetting.getProxyPort();
-            if (port == -1) {
-                port = 8080;
-            }
-            ProxyInfo proxy = ProxyInfo.buildDirectProxy(
-                    mApnSetting.getProxyAddressAsString(), port);
-            mLinkProperties.setHttpProxy(proxy);
-        } catch (NumberFormatException e) {
-            loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
-                    + mApnSetting.getProxyPort() + "): " + e);
-        }
-    }
-
-    public static class UpdateLinkPropertyResult {
-        public SetupResult setupResult = SetupResult.SUCCESS;
-        public LinkProperties oldLp;
-        public LinkProperties newLp;
-        public UpdateLinkPropertyResult(LinkProperties curLp) {
-            oldLp = curLp;
-            newLp = curLp;
-        }
-    }
-
-    /**
-     * Class returned by onSetupConnectionCompleted.
-     */
-    public enum SetupResult {
-        SUCCESS,
-        ERROR_RADIO_NOT_AVAILABLE,
-        ERROR_INVALID_ARG,
-        ERROR_STALE,
-        ERROR_DATA_SERVICE_SPECIFIC_ERROR,
-        ERROR_DUPLICATE_CID,
-        ERROR_NO_DEFAULT_CONNECTION;
-
-        public int mFailCause;
-
-        SetupResult() {
-            mFailCause = DataFailCause.getFailCause(0);
-        }
-
-        @Override
-        public String toString() {
-            return name() + "  SetupResult.mFailCause=" + DataFailCause.toString(mFailCause);
-        }
-    }
-
-    public boolean isIpv4Connected() {
-        boolean ret = false;
-        Collection <InetAddress> addresses = mLinkProperties.getAddresses();
-
-        for (InetAddress addr: addresses) {
-            if (addr instanceof java.net.Inet4Address) {
-                java.net.Inet4Address i4addr = (java.net.Inet4Address) addr;
-                if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress() &&
-                        !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) {
-                    ret = true;
-                    break;
-                }
-            }
-        }
-        return ret;
-    }
-
-    public boolean isIpv6Connected() {
-        boolean ret = false;
-        Collection <InetAddress> addresses = mLinkProperties.getAddresses();
-
-        for (InetAddress addr: addresses) {
-            if (addr instanceof java.net.Inet6Address) {
-                java.net.Inet6Address i6addr = (java.net.Inet6Address) addr;
-                if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() &&
-                        !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) {
-                    ret = true;
-                    break;
-                }
-            }
-        }
-        return ret;
-    }
-
-    public int getPduSessionId() {
-        return mPduSessionId;
-    }
-
-    public NetworkSliceInfo getSliceInfo() {
-        return mSliceInfo;
-    }
-
-    public List<TrafficDescriptor> getTrafficDescriptors() {
-        return mTrafficDescriptors;
-    }
-
-    /**
-     * Update DC fields based on a new DataCallResponse
-     * @param response the response to use to update DC fields
-     */
-    public void updateResponseFields(DataCallResponse response) {
-        updateQosParameters(response);
-        updateSliceInfo(response);
-        updateTrafficDescriptors(response);
-    }
-
-    public void updateQosParameters(final @Nullable DataCallResponse response) {
-        if (response == null) {
-            mDefaultQos = null;
-            mQosBearerSessions.clear();
-            return;
-        }
-
-        mDefaultQos = response.getDefaultQos();
-        mQosBearerSessions = response.getQosBearerSessions();
-
-        if (mNetworkAgent != null) {
-            syncQosToNetworkAgent();
-        }
-    }
-
-    private void syncQosToNetworkAgent() {
-        final DcNetworkAgent networkAgent = mNetworkAgent;
-        final List<QosBearerSession> qosBearerSessions = mQosBearerSessions;
-        if (qosBearerSessions == null) {
-            networkAgent.updateQosBearerSessions(new ArrayList<>());
-            return;
-        }
-        networkAgent.updateQosBearerSessions(qosBearerSessions);
-    }
-
-    /**
-     * Update the latest slice info on this data connection with
-     * {@link DataCallResponse#getSliceInfo}.
-     */
-    public void updateSliceInfo(DataCallResponse response) {
-        mSliceInfo = response.getSliceInfo();
-    }
-
-    /**
-     * Update the latest traffic descriptor on this data connection with
-     * {@link DataCallResponse#getTrafficDescriptors}.
-     */
-    public void updateTrafficDescriptors(DataCallResponse response) {
-        mTrafficDescriptors = response.getTrafficDescriptors();
-        mDcController.updateTrafficDescriptorsForCid(response.getId(),
-                response.getTrafficDescriptors());
-    }
-
-    @VisibleForTesting
-    public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
-        UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
-
-        if (newState == null) return result;
-
-        result.newLp = new LinkProperties();
-
-        // set link properties based on data call response
-        result.setupResult = setLinkProperties(newState, result.newLp);
-        if (result.setupResult != SetupResult.SUCCESS) {
-            if (DBG) log("updateLinkProperty failed : " + result.setupResult);
-            return result;
-        }
-        // copy HTTP proxy as it is not part DataCallResponse.
-        result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
-
-        checkSetMtu(mApnSetting, result.newLp);
-
-        mLinkProperties = result.newLp;
-
-        updateTcpBufferSizes(mRilRat);
-
-        if (DBG && (! result.oldLp.equals(result.newLp))) {
-            log("updateLinkProperty old LP=" + result.oldLp);
-            log("updateLinkProperty new LP=" + result.newLp);
-        }
-
-        if (result.newLp.equals(result.oldLp) == false &&
-                mNetworkAgent != null) {
-            mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
-        }
-
-        return result;
-    }
-
-    /**
-     * Sets the pdu session id of the data connection
-     * @param pduSessionId pdu session id to set
-     */
-    @VisibleForTesting
-    public void setPduSessionId(int pduSessionId) {
-        if (mPduSessionId != pduSessionId) {
-            logd("Changing pdu session id from: " + mPduSessionId + " to: " + pduSessionId + ", "
-                    + "Handover state: " + handoverStateToString(this.mHandoverState));
-            mPduSessionId = pduSessionId;
-        }
-    }
-
-    /**
-     * Read the MTU value from link properties where it can be set from network. In case
-     * not set by the network, set it again using the mtu szie value defined in the APN
-     * database for the connected APN
-     */
-    private void checkSetMtu(ApnSetting apn, LinkProperties lp) {
-        if (lp == null) return;
-
-        if (apn == null || lp == null) return;
-
-        if (lp.getMtu() != PhoneConstants.UNSET_MTU) {
-            if (DBG) log("MTU set by call response to: " + lp.getMtu());
-            return;
-        }
-
-        if (apn != null && apn.getMtuV4() != PhoneConstants.UNSET_MTU) {
-            lp.setMtu(apn.getMtuV4());
-            if (DBG) log("MTU set by APN to: " + apn.getMtuV4());
-            return;
-        }
-
-        int mtu = mPhone.getContext().getResources().getInteger(
-                com.android.internal.R.integer.config_mobile_mtu);
-        if (mtu != PhoneConstants.UNSET_MTU) {
-            lp.setMtu(mtu);
-            if (DBG) log("MTU set by config resource to: " + mtu);
-        }
-    }
-
-    //***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
-    private DataConnection(Phone phone, String tagSuffix, int id,
-                           DcTracker dct, DataServiceManager dataServiceManager,
-                           DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
-        super("DC-" + tagSuffix, dcc);
-        mTagSuffix = tagSuffix;
-        setLogRecSize(300);
-        setLogOnlyTransitions(true);
-        if (DBG) log("DataConnection created");
-
-        mPhone = phone;
-        mDct = dct;
-        mDataServiceManager = dataServiceManager;
-        mVcnManager = mPhone.getContext().getSystemService(VcnManager.class);
-        mTransportType = dataServiceManager.getTransportType();
-        mDcTesterFailBringUpAll = failBringUpAll;
-        mDcController = dcc;
-        mId = id;
-        mCid = -1;
-        mDataRegState = mPhone.getServiceState().getDataRegistrationState();
-        mIsSuspended = false;
-        mDataCallSessionStats = new DataCallSessionStats(mPhone);
-        mDoAllocatePduSessionId = false;
-
-        int networkType = getNetworkType();
-        mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType);
-        updateLinkBandwidthsFromCarrierConfig(mRilRat);
-
-        addState(mDefaultState);
-            addState(mInactiveState, mDefaultState);
-            addState(mActivatingState, mDefaultState);
-            addState(mActiveState, mDefaultState);
-            addState(mDisconnectingState, mDefaultState);
-            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
-        setInitialState(mInactiveState);
-    }
-
-    private @NetworkType int getNetworkType() {
-        ServiceState ss = mPhone.getServiceState();
-        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-
-        NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
-        if (nri != null) {
-            networkType = nri.getAccessNetworkTechnology();
-        }
-
-        return networkType;
-    }
-
-    /**
-     * Get the source transport for handover. For example, handover from WWAN to WLAN, WWAN is the
-     * source transport, and vice versa.
-     */
-    private @TransportType int getHandoverSourceTransport() {
-        return mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-    }
-
-    /**
-     * API to generate the OsAppId for enterprise traffic category.
-     * @return byte[] representing OsId + length of OsAppId + OsAppId
-     */
-    @VisibleForTesting
-    public static byte[] getEnterpriseOsAppId() {
-        byte[] osAppId = NetworkCapabilities.getCapabilityCarrierName(
-                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE).getBytes();
-        // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
-        ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
-        bb.putLong(OS_ID.getMostSignificantBits());
-        bb.putLong(OS_ID.getLeastSignificantBits());
-        bb.put((byte) osAppId.length);
-        bb.put(osAppId);
-        if (VDBG) {
-            Rlog.d("DataConnection", "getEnterpriseOsAppId: "
-                    + IccUtils.bytesToHexString(bb.array()));
-        }
-        return bb.array();
-    }
-
-    /**
-     * Begin setting up a data connection, calls setupDataCall
-     * and the ConnectionParams will be returned with the
-     * EVENT_SETUP_DATA_CONNECTION_DONE
-     *
-     * @param cp is the connection parameters
-     *
-     * @return Fail cause if failed to setup data connection. {@link DataFailCause#NONE} if success.
-     */
-    private @DataFailureCause int connect(ConnectionParams cp) {
-        log("connect: carrier='" + mApnSetting.getEntryName()
-                + "' APN='" + mApnSetting.getApnName()
-                + "' proxy='" + mApnSetting.getProxyAddressAsString()
-                + "' port='" + mApnSetting.getProxyPort() + "'");
-        ApnContext.requestLog(cp.mApnContext, "DataConnection.connect");
-
-        // Check if we should fake an error.
-        if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
-            DataCallResponse response = new DataCallResponse.Builder()
-                    .setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause)
-                    .setRetryDurationMillis(
-                            mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime)
-                    .setMtuV4(PhoneConstants.UNSET_MTU)
-                    .setMtuV6(PhoneConstants.UNSET_MTU)
-                    .build();
-
-            Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
-            AsyncResult.forMessage(msg, response, null);
-            sendMessage(msg);
-            if (DBG) {
-                log("connect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp()
-                        + " send error response=" + response);
-            }
-            mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1;
-            return DataFailCause.NONE;
-        }
-
-        mCreateTime = -1;
-        mLastFailTime = -1;
-        mLastFailCause = DataFailCause.NONE;
-
-        Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
-        msg.obj = cp;
-
-        DataProfile dp = new DataProfile.Builder()
-                .setApnSetting(mApnSetting)
-                .setPreferred(cp.mIsPreferredApn)
-                .build();
-
-        // We need to use the actual modem roaming state instead of the framework roaming state
-        // here. This flag is only passed down to ril_service for picking the correct protocol (for
-        // old modem backward compatibility).
-        boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
-
-        // If the apn is NOT metered, we will allow data roaming regardless of the setting.
-        boolean isUnmeteredApnType = !ApnSettingUtils.isMeteredApnType(
-                cp.mApnContext.getApnTypeBitmask(), mPhone);
-
-        // Set this flag to true if the user turns on data roaming. Or if we override the roaming
-        // state in framework, we should set this flag to true as well so the modem will not reject
-        // the data call setup (because the modem actually thinks the device is roaming).
-        boolean allowRoaming = mPhone.getDataRoamingEnabled()
-                || (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()
-                || isUnmeteredApnType));
-
-        String dnn = null;
-        byte[] osAppId = null;
-        if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-            osAppId = getEnterpriseOsAppId();
-        } else {
-            dnn = mApnSetting.getApnName();
-        }
-        final TrafficDescriptor td = osAppId == null && dnn == null ? null
-                : new TrafficDescriptor(dnn, osAppId);
-        final boolean matchAllRuleAllowed = td == null || td.getOsAppId() == null;
-
-        if (DBG) {
-            log("allowRoaming=" + allowRoaming
-                    + ", mPhone.getDataRoamingEnabled()=" + mPhone.getDataRoamingEnabled()
-                    + ", isModemRoaming=" + isModemRoaming
-                    + ", mPhone.getServiceState().getDataRoaming()="
-                    + mPhone.getServiceState().getDataRoaming()
-                    + ", isUnmeteredApnType=" + isUnmeteredApnType
-                    + ", trafficDescriptor=" + td
-                    + ", matchAllRuleAllowed=" + matchAllRuleAllowed
-            );
-        }
-
-        // Check if this data setup is a handover.
-        LinkProperties linkProperties = null;
-        int reason = DataService.REQUEST_REASON_NORMAL;
-        if (cp.mRequestType == REQUEST_TYPE_HANDOVER) {
-            // If this is a data setup for handover, we need to pass the link properties
-            // of the existing data connection to the modem.
-            DcTracker srcDcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
-            if (srcDcTracker == null || cp.mApnContext == null) {
-                loge("connect: Handover failed. dcTracker=" + srcDcTracker + ", apnContext="
-                        + cp.mApnContext);
-                return DataFailCause.HANDOVER_FAILED;
-            }
-
-
-            // srcDc is the source data connection while the current instance is the target
-            DataConnection srcDc =
-                    srcDcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType());
-            if (srcDc == null) {
-                loge("connect: Can't find data connection for handover.");
-                return DataFailCause.HANDOVER_FAILED;
-            }
-
-            // Helpful for logging purposes
-            DataServiceManager srcDsm = srcDc.mDataServiceManager;
-            String srcDsmTag = (srcDsm == null ? "(null)" : srcDsm.getTag());
-            logd("connect: REQUEST_TYPE_HANDOVER - Request handover from " + srcDc.getName()
-                    + ", targetDsm=" + mDataServiceManager.getTag()
-                    + ", sourceDsm=" + srcDsmTag);
-
-
-            /* startHandover is called on the source data connection, and if successful,
-               we ask the target data connection (which is the current instance) to call
-               #setupDataCall with request type handover.
-            */
-            Consumer<Integer> onCompleted = (dataServiceCallbackResultCode) ->
-                    /* startHandover is called on the srcDc handler, but the callback needs to
-                       be called on the current (which is the targetDc) handler which is why we
-                       call sendRunnableMessage. */
-                    sendRunnableMessage(EVENT_START_HANDOVER_ON_TARGET,
-                        (inCorrectState) -> requestHandover(inCorrectState, srcDc,
-                            dataServiceCallbackResultCode,
-                            cp, msg, dp, isModemRoaming, allowRoaming));
-            srcDc.startHandover(onCompleted);
-            return DataFailCause.NONE;
-        }
-
-        // setup data call for REQUEST_TYPE_NORMAL
-        mDoAllocatePduSessionId = mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
-        allocatePduSessionId(psi -> {
-            this.setPduSessionId(psi);
-            mDataServiceManager.setupDataCall(
-                    ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
-                    dp,
-                    isModemRoaming,
-                    allowRoaming,
-                    reason,
-                    linkProperties,
-                    psi,
-                    null, //slice info is null since this is not a handover
-                    td,
-                    matchAllRuleAllowed,
-                    msg);
-            TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
-                    dp.getProfileId(), dp.getApn(), dp.getProtocolType());
-        });
-        return DataFailCause.NONE;
-    }
-
-    private void allocatePduSessionId(Consumer<Integer> allocateCallback) {
-        if (mDoAllocatePduSessionId) {
-            Message msg = this.obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID);
-            msg.obj = allocateCallback;
-            mPhone.mCi.allocatePduSessionId(msg);
-        } else {
-            allocateCallback.accept(PDU_SESSION_ID_NOT_SET);
-        }
-    }
-
-    private void onRquestHandoverFailed(ConnectionParams cp) {
-        sendMessage(obtainMessage(EVENT_CANCEL_HANDOVER));
-        notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
-                DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
-    }
-
-    private void requestHandover(boolean inCorrectState, DataConnection srcDc,
-            @DataServiceCallback.ResultCode int resultCode,
-            ConnectionParams cp, Message msg, DataProfile dp, boolean isModemRoaming,
-            boolean allowRoaming) {
-
-        if (!inCorrectState) {
-            logd("requestHandover: Not in correct state");
-            if (isResultCodeSuccess(resultCode)) {
-                if (srcDc != null) {
-                    logd("requestHandover: Not in correct state - Success result code");
-                    // We need to cancel the handover on source if we ended up in the wrong state.
-                    srcDc.cancelHandover();
-                } else {
-                    logd("requestHandover: Not in correct state - Success result code - "
-                            + "srcdc = null");
-                }
-            }
-            onRquestHandoverFailed(cp);
-            return;
-        } else if (!isResultCodeSuccess(resultCode)) {
-            if (DBG) {
-                logd("requestHandover: Non success result code from DataService, "
-                        + "setupDataCall will not be called, result code = "
-                        + DataServiceCallback.resultCodeToString(resultCode));
-            }
-            onRquestHandoverFailed(cp);
-            return;
-        }
-
-        if (srcDc == null) {
-            loge("requestHandover: Cannot find source data connection.");
-            onRquestHandoverFailed(cp);
-            return;
-        }
-
-        LinkProperties linkProperties;
-        int reason;
-
-        // Preserve the potential network agent from the source data connection. The ownership
-        // is not transferred at this moment.
-        mHandoverSourceNetworkAgent = srcDc.getNetworkAgent();
-        if (mHandoverSourceNetworkAgent == null) {
-            loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName());
-            onRquestHandoverFailed(cp);
-            return;
-        }
-
-        linkProperties = srcDc.getLinkProperties();
-        if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) {
-            loge("requestHandover: Can't find link properties of handover data connection. dc="
-                    + srcDc);
-            onRquestHandoverFailed(cp);
-            return;
-        }
-
-        mHandoverLocalLog.log("Handover started. Preserved the agent.");
-        log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
-
-        reason = DataService.REQUEST_REASON_HANDOVER;
-
-        TrafficDescriptor td = dp.getApn() == null ? null
-                : new TrafficDescriptor(dp.getApn(), null);
-        boolean matchAllRuleAllowed = true;
-
-        mDataServiceManager.setupDataCall(
-                ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
-                dp,
-                isModemRoaming,
-                allowRoaming,
-                reason,
-                linkProperties,
-                srcDc.getPduSessionId(),
-                srcDc.getSliceInfo(),
-                td,
-                matchAllRuleAllowed,
-                msg);
-        TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
-                dp.getProfileId(), dp.getApn(), dp.getProtocolType());
-    }
-
-    /**
-     * Called on the source data connection from the target data connection.
-     */
-    @VisibleForTesting
-    public void startHandover(Consumer<Integer> onTargetDcComplete) {
-        logd("startHandover: " + toStringSimple());
-        // Set the handover state to being transferred on "this" data connection which is the src.
-        setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
-
-        Consumer<Integer> onSrcDcComplete =
-                resultCode -> onHandoverStarted(resultCode, onTargetDcComplete);
-        /*
-            The flow here is:
-            srcDc#startHandover -> dataService#startHandover -> (onHandoverStarted) ->
-                onSrcDcComplete -> onTargetDcComplete
-         */
-        mDataServiceManager.startHandover(mCid,
-                this.obtainMessage(EVENT_START_HANDOVER,
-                        onSrcDcComplete));
-    }
-
-    /**
-     * Called on the source data connection when the async call to start handover is complete
-     */
-    private void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode,
-            Consumer<Integer> onTargetDcComplete) {
-        logd("onHandoverStarted: " + toStringSimple());
-        if (!isResultCodeSuccess(resultCode)) {
-            setHandoverState(HANDOVER_STATE_IDLE);
-        }
-        onTargetDcComplete.accept(resultCode);
-    }
-
-    private void cancelHandover() {
-        if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) {
-            logd("cancelHandover: handover state is " + handoverStateToString(mHandoverState)
-                    + ", expecting HANDOVER_STATE_BEING_TRANSFERRED");
-        }
-        mDataServiceManager.cancelHandover(mCid, this.obtainMessage(EVENT_CANCEL_HANDOVER));
-        setHandoverState(HANDOVER_STATE_IDLE);
-    }
-
-    /**
-     * Update NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED based on congested override
-     * @param isCongested whether this DC should be set to congested or not
-     */
-    public void onCongestednessChanged(boolean isCongested) {
-        sendMessage(obtainMessage(EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED, isCongested));
-    }
-
-    /**
-     * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on metered override
-     * @param isUnmetered whether this DC should be set to unmetered or not
-     */
-    public void onMeterednessChanged(boolean isUnmetered) {
-        sendMessage(obtainMessage(EVENT_DATA_CONNECTION_METEREDNESS_CHANGED, isUnmetered));
-    }
-
-    /**
-     * TearDown the data connection when the deactivation is complete a Message with
-     * msg.what == EVENT_DEACTIVATE_DONE
-     *
-     * @param o is the object returned in the AsyncResult.obj.
-     */
-    private void tearDownData(Object o) {
-        int discReason = DataService.REQUEST_REASON_NORMAL;
-        ApnContext apnContext = null;
-        if ((o != null) && (o instanceof DisconnectParams)) {
-            DisconnectParams dp = (DisconnectParams) o;
-            apnContext = dp.mApnContext;
-            if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
-                    || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
-                discReason = DataService.REQUEST_REASON_SHUTDOWN;
-            } else if (dp.mReleaseType == DcTracker.RELEASE_TYPE_HANDOVER) {
-                discReason = DataService.REQUEST_REASON_HANDOVER;
-            }
-        }
-
-        String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
-        if (DBG) log(str);
-        ApnContext.requestLog(apnContext, str);
-
-
-        //Needed to be final to work in a closure
-        final int fDiscReason = discReason;
-        releasePduSessionId(() -> {
-            // This is run after release pdu session id is complete
-            this.setPduSessionId(PDU_SESSION_ID_NOT_SET);
-            mDataServiceManager.deactivateDataCall(mCid, fDiscReason,
-                    obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
-            mDataCallSessionStats.setDeactivateDataCallReason(fDiscReason);
-        });
-    }
-
-    private void releasePduSessionId(Runnable releaseCallback) {
-        // If the transport is IWLAN, and there is a valid PDU session id, also the data connection
-        // is not being handovered, we should release the pdu session id.
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                && mHandoverState == HANDOVER_STATE_IDLE
-                && this.getPduSessionId() != PDU_SESSION_ID_NOT_SET) {
-            Message msg = this.obtainMessage(EVENT_RELEASE_PDU_SESSION_ID);
-            msg.obj = releaseCallback;
-            mPhone.mCi.releasePduSessionId(msg, this.getPduSessionId());
-        } else {
-            // Just go and run the callback since we either have no pdu session id to release
-            // or we are in the middle of a handover
-            releaseCallback.run();
-        }
-    }
-
-    private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
-        for (ConnectionParams cp : mApnContexts.values()) {
-            ApnContext apnContext = cp.mApnContext;
-            if (apnContext == alreadySent) continue;
-            if (reason != null) apnContext.setReason(reason);
-            Pair<ApnContext, Integer> pair = new Pair<>(apnContext, cp.mConnectionGeneration);
-            Message msg = mDct.obtainMessage(event, cp.mRequestType,
-                    DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, pair);
-            AsyncResult.forMessage(msg);
-            msg.sendToTarget();
-        }
-    }
-
-    /**
-     * Send the connectionCompletedMsg.
-     *
-     * @param cp is the ConnectionParams
-     * @param cause and if no error the cause is DataFailCause.NONE
-     * @param handoverFailureMode The action on handover failure
-     * @param sendAll is true if all contexts are to be notified
-     */
-    private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause,
-            @HandoverFailureMode int handoverFailureMode, boolean sendAll) {
-        ApnContext alreadySent = null;
-
-        if (cp != null && cp.mOnCompletedMsg != null) {
-            // Get the completed message but only use it once
-            Message connectionCompletedMsg = cp.mOnCompletedMsg;
-            cp.mOnCompletedMsg = null;
-            alreadySent = cp.mApnContext;
-
-            long timeStamp = System.currentTimeMillis();
-            connectionCompletedMsg.arg1 = cp.mRequestType;
-            connectionCompletedMsg.arg2 = handoverFailureMode;
-
-            if (cause == DataFailCause.NONE) {
-                mCreateTime = timeStamp;
-                AsyncResult.forMessage(connectionCompletedMsg);
-            } else {
-                mLastFailCause = cause;
-                mLastFailTime = timeStamp;
-
-                // Return message with a Throwable exception to signify an error.
-                if (cause == DataFailCause.NONE) cause = DataFailCause.UNKNOWN;
-                AsyncResult.forMessage(connectionCompletedMsg, cause,
-                        new Throwable(DataFailCause.toString(cause)));
-            }
-            if (DBG) {
-                log("notifyConnectCompleted at " + timeStamp + " cause="
-                        + DataFailCause.toString(cause) + " connectionCompletedMsg="
-                        + msgToString(connectionCompletedMsg));
-            }
-
-            connectionCompletedMsg.sendToTarget();
-        }
-        if (sendAll) {
-            log("Send to all. " + alreadySent + " " + DataFailCause.toString(cause));
-            notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR,
-                    DataFailCause.toString(cause));
-        }
-    }
-
-    /**
-     * Send ar.userObj if its a message, which is should be back to originator.
-     *
-     * @param dp is the DisconnectParams.
-     */
-    private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
-        if (VDBG) log("NotifyDisconnectCompleted");
-
-        ApnContext alreadySent = null;
-        String reason = null;
-
-        if (dp != null && dp.mOnCompletedMsg != null) {
-            // Get the completed message but only use it once
-            Message msg = dp.mOnCompletedMsg;
-            dp.mOnCompletedMsg = null;
-            if (msg.obj instanceof ApnContext) {
-                alreadySent = (ApnContext)msg.obj;
-            }
-            reason = dp.mReason;
-            if (VDBG) {
-                log(String.format("msg=%s msg.obj=%s", msg.toString(),
-                    ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
-            }
-            AsyncResult.forMessage(msg);
-            msg.sendToTarget();
-        }
-        if (sendAll) {
-            if (reason == null) {
-                reason = DataFailCause.toString(DataFailCause.UNKNOWN);
-            }
-            notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason);
-        }
-        if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
-    }
-
-    private void sendRunnableMessage(int eventCode, @NonNull final Consumer<Boolean> r) {
-        sendMessage(eventCode, r);
-    }
-
-    /*
-     * **************************************************************************
-     * Begin Members and methods owned by DataConnectionTracker but stored
-     * in a DataConnection because there is one per connection.
-     * **************************************************************************
-     */
-
-    /*
-     * The id is owned by DataConnectionTracker.
-     */
-    private int mId;
-
-    /**
-     * Get the DataConnection ID
-     */
-    public int getDataConnectionId() {
-        return mId;
-    }
-
-    /*
-     * **************************************************************************
-     * End members owned by DataConnectionTracker
-     * **************************************************************************
-     */
-
-    /**
-     * Clear all settings called when entering mInactiveState.
-     */
-    private synchronized void clearSettings() {
-        if (DBG) log("clearSettings");
-
-        mCreateTime = -1;
-        mLastFailTime = -1;
-        mLastFailCause = DataFailCause.NONE;
-        mCid = -1;
-
-        mPcscfAddr = new String[5];
-
-        mLinkProperties = new LinkProperties();
-        mApnContexts.clear();
-        mApnSetting = null;
-        mUnmeteredUseOnly = false;
-        mMmsUseOnly = false;
-        mEnterpriseUse = false;
-        mRestrictedNetworkOverride = false;
-        mDcFailCause = DataFailCause.NONE;
-        mDisabledApnTypeBitMask = 0;
-        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        mCongestedOverride = false;
-        mUnmeteredOverride = false;
-        mDownlinkBandwidth = 14;
-        mUplinkBandwidth = 14;
-        mIsSuspended = false;
-        mHandoverState = HANDOVER_STATE_IDLE;
-        mHandoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN;
-        mSliceInfo = null;
-        mDefaultQos = null;
-        mDoAllocatePduSessionId = false;
-        mQosBearerSessions.clear();
-        mTrafficDescriptors.clear();
-    }
-
-    /**
-     * Process setup data completion result from data service
-     *
-     * @param resultCode The result code returned by data service
-     * @param response Data call setup response from data service
-     * @param cp The original connection params used for data call setup
-     * @return Setup result
-     */
-    private SetupResult onSetupConnectionCompleted(@DataServiceCallback.ResultCode int resultCode,
-                                                   DataCallResponse response,
-                                                   ConnectionParams cp) {
-        SetupResult result;
-
-        log("onSetupConnectionCompleted: resultCode=" + resultCode + ", response=" + response);
-        if (cp.mTag != mTag) {
-            if (DBG) {
-                log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
-            }
-            result = SetupResult.ERROR_STALE;
-        } else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) {
-            result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
-            result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
-        } else if (resultCode == DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE) {
-            result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
-            result.mFailCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE;
-        } else if (resultCode == DataServiceCallback.RESULT_ERROR_INVALID_ARG) {
-            result = SetupResult.ERROR_INVALID_ARG;
-            result.mFailCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER;
-        } else if (response.getCause() != 0) {
-            if (response.getCause() == DataFailCause.RADIO_NOT_AVAILABLE) {
-                result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
-                result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
-            } else {
-                result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
-                result.mFailCause = DataFailCause.getFailCause(response.getCause());
-            }
-        } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
-                && mDcController.getActiveDcByCid(response.getId()) != null) {
-            if (!mDcController.getTrafficDescriptorsForCid(response.getId())
-                    .equals(response.getTrafficDescriptors())) {
-                if (DBG) log("Updating traffic descriptors: " + response.getTrafficDescriptors());
-                mDcController.getActiveDcByCid(response.getId()).updateTrafficDescriptors(response);
-                mDct.obtainMessage(DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED).sendToTarget();
-            }
-            if (DBG) log("DataConnection already exists for cid: " + response.getId());
-            result = SetupResult.ERROR_DUPLICATE_CID;
-            result.mFailCause = DataFailCause.DUPLICATE_CID;
-        } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
-                && !mDcController.isDefaultDataActive()) {
-            if (DBG) log("No default data connection currently active");
-            mCid = response.getId();
-            result = SetupResult.ERROR_NO_DEFAULT_CONNECTION;
-            result.mFailCause = DataFailCause.NO_DEFAULT_DATA;
-        } else {
-            if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
-            mCid = response.getId();
-            setPduSessionId(response.getPduSessionId());
-            updatePcscfAddr(response);
-            updateResponseFields(response);
-            result = updateLinkProperty(response).setupResult;
-        }
-
-        return result;
-    }
-
-    private static boolean isResultCodeSuccess(int resultCode) {
-        return resultCode == DataServiceCallback.RESULT_SUCCESS
-                || resultCode == DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
-    }
-
-    private boolean isDnsOk(String[] domainNameServers) {
-        if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
-                && !mPhone.isDnsCheckDisabled()) {
-            // Work around a race condition where QMI does not fill in DNS:
-            // Deactivate PDP and let DataConnectionTracker retry.
-            // Do not apply the race condition workaround for MMS APN
-            // if Proxy is an IP-address.
-            // Otherwise, the default APN will not be restored anymore.
-            if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
-                log(String.format(
-                        "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
-                        mApnSetting.getApnTypeBitmask(), ApnSetting.TYPE_MMS_STRING,
-                        mApnSetting.getMmsProxyAddressAsString(),
-                        isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * TCP buffer size config based on the ril technology. There are 6 parameters
-     * read_min, read_default, read_max, write_min, write_default, write_max in the TCP buffer
-     * config string and they are separated by a comma. The unit of these parameters is byte.
-     */
-    private static final String TCP_BUFFER_SIZES_GPRS = "4092,8760,48000,4096,8760,48000";
-    private static final String TCP_BUFFER_SIZES_EDGE = "4093,26280,70800,4096,16384,70800";
-    private static final String TCP_BUFFER_SIZES_UMTS = "58254,349525,1048576,58254,349525,1048576";
-    private static final String TCP_BUFFER_SIZES_1XRTT = "16384,32768,131072,4096,16384,102400";
-    private static final String TCP_BUFFER_SIZES_EVDO = "4094,87380,262144,4096,16384,262144";
-    private static final String TCP_BUFFER_SIZES_EHRPD = "131072,262144,1048576,4096,16384,524288";
-    private static final String TCP_BUFFER_SIZES_HSDPA = "61167,367002,1101005,8738,52429,262114";
-    private static final String TCP_BUFFER_SIZES_HSPA = "40778,244668,734003,16777,100663,301990";
-    private static final String TCP_BUFFER_SIZES_LTE =
-            "524288,1048576,2097152,262144,524288,1048576";
-    private static final String TCP_BUFFER_SIZES_HSPAP =
-            "122334,734003,2202010,32040,192239,576717";
-    private static final String TCP_BUFFER_SIZES_NR =
-            "2097152,6291456,16777216,512000,2097152,8388608";
-    private static final String TCP_BUFFER_SIZES_LTE_CA =
-            "4096,6291456,12582912,4096,1048576,2097152";
-
-    private void updateTcpBufferSizes(int rilRat) {
-        String sizes = null;
-        ServiceState ss = mPhone.getServiceState();
-        if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE &&
-                ss.isUsingCarrierAggregation()) {
-            rilRat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
-        }
-        String ratName = ServiceState.rilRadioTechnologyToString(rilRat).toLowerCase(Locale.ROOT);
-        // ServiceState gives slightly different names for EVDO tech ("evdo-rev.0" for ex)
-        // - patch it up:
-        if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
-                rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A ||
-                rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B) {
-            ratName = RAT_NAME_EVDO;
-        }
-
-        // NR 5G Non-Standalone use LTE cell as the primary cell, the ril technology is LTE in this
-        // case. We use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE ||
-                rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected())
-                && mPhone.getServiceStateTracker().getNrContextIds().contains(mCid)) {
-            ratName = RAT_NAME_5G;
-        }
-
-        log("updateTcpBufferSizes: " + ratName);
-
-        // in the form: "ratname:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
-        String[] configOverride = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_mobile_tcp_buffers);
-        for (int i = 0; i < configOverride.length; i++) {
-            String[] split = configOverride[i].split(":");
-            if (ratName.equals(split[0]) && split.length == 2) {
-                sizes = split[1];
-                break;
-            }
-        }
-
-        if (sizes == null) {
-            // no override - use telephony defaults
-            // doing it this way allows device or carrier to just override the types they
-            // care about and inherit the defaults for the others.
-            switch (rilRat) {
-                case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
-                    sizes = TCP_BUFFER_SIZES_GPRS;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
-                    sizes = TCP_BUFFER_SIZES_EDGE;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
-                    sizes = TCP_BUFFER_SIZES_UMTS;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
-                    sizes = TCP_BUFFER_SIZES_1XRTT;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
-                case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
-                case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
-                    sizes = TCP_BUFFER_SIZES_EVDO;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
-                    sizes = TCP_BUFFER_SIZES_EHRPD;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
-                    sizes = TCP_BUFFER_SIZES_HSDPA;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
-                case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
-                    sizes = TCP_BUFFER_SIZES_HSPA;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
-                    // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
-                    if (RAT_NAME_5G.equals(ratName)) {
-                        sizes = TCP_BUFFER_SIZES_NR;
-                    } else {
-                        sizes = TCP_BUFFER_SIZES_LTE;
-                    }
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
-                    // Use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
-                    if (RAT_NAME_5G.equals(ratName)) {
-                        sizes = TCP_BUFFER_SIZES_NR;
-                    } else {
-                        sizes = TCP_BUFFER_SIZES_LTE_CA;
-                    }
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
-                    sizes = TCP_BUFFER_SIZES_HSPAP;
-                    break;
-                case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
-                    sizes = TCP_BUFFER_SIZES_NR;
-                    break;
-                default:
-                    // Leave empty - this will let ConnectivityService use the system default.
-                    break;
-            }
-        }
-        mLinkProperties.setTcpBufferSizes(sizes);
-    }
-
-    private void updateLinkBandwidthsFromCarrierConfig(int rilRat) {
-        String ratName = DataConfigManager.getDataConfigNetworkType(
-                mPhone.getDisplayInfoController().getTelephonyDisplayInfo());
-
-        if (DBG) log("updateLinkBandwidthsFromCarrierConfig: " + ratName);
-
-        Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
-        if (values == null) {
-            values = new Pair<>(14, 14);
-        }
-        mDownlinkBandwidth = values.first;
-        mUplinkBandwidth = values.second;
-    }
-
-
-    private void updateLinkBandwidthsFromModem(List<LinkCapacityEstimate> lceList) {
-        if (DBG) log("updateLinkBandwidthsFromModem: lceList=" + lceList);
-        boolean downlinkUpdated = false;
-        boolean uplinkUpdated = false;
-        LinkCapacityEstimate lce = lceList.get(0);
-        // LCE status deprecated in IRadio 1.2, so only check for IRadio < 1.2
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)
-                || mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
-            if (lce.getDownlinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
-                mDownlinkBandwidth = lce.getDownlinkCapacityKbps();
-                downlinkUpdated = true;
-            }
-            if (lce.getUplinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
-                mUplinkBandwidth = lce.getUplinkCapacityKbps();
-                uplinkUpdated = true;
-            }
-        }
-
-        if (!downlinkUpdated || !uplinkUpdated) {
-            fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
-        }
-
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
-        }
-    }
-
-    private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps,
-            int downlinkBandwidthKbps) {
-        if (DBG) {
-            log("updateLinkBandwidthsFromBandwidthEstimator, UL= "
-                    + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps);
-        }
-        boolean downlinkUpdated = false;
-        boolean uplinkUpdated = false;
-        if (downlinkBandwidthKbps > 0) {
-            mDownlinkBandwidth = downlinkBandwidthKbps;
-            downlinkUpdated = true;
-        }
-        if (uplinkBandwidthKbps > 0) {
-            mUplinkBandwidth = uplinkBandwidthKbps;
-            uplinkUpdated = true;
-        }
-
-        if (!downlinkUpdated || !uplinkUpdated) {
-            fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
-        }
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
-        }
-    }
-
-    private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) {
-        String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
-        if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
-            ratName = mPhone.getServiceState().getNrFrequencyRange()
-                    == ServiceState.FREQUENCY_RANGE_MMWAVE
-                    ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
-        }
-        Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
-        if (values != null) {
-            if (!downlinkUpdated) {
-                mDownlinkBandwidth = values.first;
-            }
-            if (!uplinkUpdated) {
-                mUplinkBandwidth = values.second;
-            }
-            mUplinkBandwidth = Math.min(mUplinkBandwidth, mDownlinkBandwidth);
-        }
-    }
-
-    private boolean isBandwidthSourceKey(String source) {
-        return source.equals(mPhone.getContext().getResources().getString(
-                com.android.internal.R.string.config_bandwidthEstimateSource));
-    }
-
-    /**
-     * Indicates if this data connection was established for unmetered use only. Note that this
-     * flag should be populated when data becomes active. And if it is set to true, it can be set to
-     * false later when we are reevaluating the data connection. But if it is set to false, it
-     * can never become true later because setting it to true will cause this data connection
-     * losing some immutable network capabilities, which can cause issues in connectivity service.
-     */
-    private boolean mUnmeteredUseOnly = false;
-
-    /**
-     * Indicates if this data connection was established for MMS use only. This is true only when
-     * mobile data is disabled but the user allows sending and receiving MMS messages. If the data
-     * enabled settings indicate that MMS data is allowed unconditionally, MMS can be sent when data
-     * is disabled even if it is a metered APN type.
-     */
-    private boolean mMmsUseOnly = false;
-
-    /**
-     * Indicates if when this connection was established we had a restricted/privileged
-     * NetworkRequest and needed it to overcome data-enabled limitations.
-     *
-     * This flag overrides the APN-based restriction capability, restricting the network
-     * based on both having a NetworkRequest with restricted AND needing a restricted
-     * bit to overcome user-disabled status.  This allows us to handle the common case
-     * of having both restricted requests and unrestricted requests for the same apn:
-     * if conditions require a restricted network to overcome user-disabled then it must
-     * be restricted, otherwise it is unrestricted (or restricted based on APN type).
-     *
-     * This supports a privileged app bringing up a network without general apps having access
-     * to it when the network is otherwise unavailable (hipri).  The first use case is
-     * pre-paid SIM reprovisioning over internet, where the carrier insists on no traffic
-     * other than from the privileged carrier-app.
-     *
-     * Note that the data connection cannot go from unrestricted to restricted because the
-     * connectivity service does not support dynamically closing TCP connections at this point.
-     */
-    private boolean mRestrictedNetworkOverride = false;
-
-    /**
-     * Indicates if this data connection supports enterprise use. Note that this flag should be
-     * populated when data becomes active. Once it is set, the value cannot be changed because
-     * setting it will cause this data connection to lose immutable network capabilities, which can
-     * cause issues in connectivity service.
-     */
-    private boolean mEnterpriseUse = false;
-
-    /**
-     * Check if this data connection should be restricted. We should call this when data connection
-     * becomes active, or when we want to re-evaluate the conditions to decide if we need to
-     * unstrict the data connection.
-     *
-     * @return True if this data connection needs to be restricted.
-     */
-    private boolean shouldRestrictNetwork() {
-        // first, check if there is any network request that containing restricted capability
-        // (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request)
-        boolean isAnyRestrictedRequest = false;
-        for (ApnContext apnContext : mApnContexts.keySet()) {
-            if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) {
-                isAnyRestrictedRequest = true;
-                break;
-            }
-        }
-
-        // If all of the network requests are non-restricted, then we don't need to restrict
-        // the network.
-        if (!isAnyRestrictedRequest) {
-            return false;
-        }
-
-        // If the network is unmetered, then we don't need to restrict the network because users
-        // won't be charged anyway.
-        if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
-            return false;
-        }
-
-        // If the data is disabled, then we need to restrict the network so only privileged apps can
-        // use the restricted network while data is disabled.
-        if (!mPhone.getDataEnabledSettings().isDataEnabled()) {
-            return true;
-        }
-
-        // If the device is roaming, and the user does not turn on data roaming, then we need to
-        // restrict the network so only privileged apps can use it.
-        if (!mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
-            return true;
-        }
-
-        // Otherwise we should not restrict the network so anyone who requests can use it.
-        return false;
-    }
-
-    /**
-     * @return True if this data connection should only be used for unmetered purposes.
-     */
-    private boolean isUnmeteredUseOnly() {
-        // If this data connection is on IWLAN, then it's unmetered and can be used by everyone.
-        // Should not be for unmetered used only.
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-            return false;
-        }
-
-        // If data is enabled, this data connection can't be for unmetered used only because
-        // everyone should be able to use it if:
-        // 1. Device is not roaming, or
-        // 2. Device is roaming and data roaming is turned on
-        if (mPhone.getDataEnabledSettings().isDataEnabled()) {
-            if (!mPhone.getServiceState().getDataRoaming() || mDct.getDataRoamingEnabled()) {
-                return false;
-            }
-        }
-
-        // The data connection can only be unmetered used only if all attached APN contexts
-        // attached to this data connection are unmetered.
-        for (ApnContext apnContext : mApnContexts.keySet()) {
-            if (ApnSettingUtils.isMeteredApnType(apnContext.getApnTypeBitmask(), mPhone)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * @return True if this data connection should only be used for MMS purposes.
-     */
-    private boolean isMmsUseOnly() {
-        // MMS use only if data is disabled, MMS is allowed unconditionally, and MMS is the only
-        // APN type for this data connection.
-        DataEnabledSettings des = mPhone.getDataEnabledSettings();
-        boolean mmsAllowedUnconditionally = !des.isDataEnabled() && des.isMmsAlwaysAllowed();
-        boolean mmsApnOnly = isApnContextAttached(ApnSetting.TYPE_MMS, true);
-        return mmsAllowedUnconditionally && mmsApnOnly;
-    }
-
-    /**
-     * Check if this data connection supports enterprise use. We call this when the data connection
-     * becomes active or when we want to reevaluate the conditions to decide if we need to update
-     * the network agent capabilities.
-     *
-     * @return True if this data connection supports enterprise use.
-     */
-    private boolean isEnterpriseUse() {
-        return  mApnContexts.keySet().stream().anyMatch(
-                ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE);
-    }
-
-    /**
-     * Get the network capabilities for this data connection.
-     *
-     * @return the {@link NetworkCapabilities} of this data connection.
-     */
-    public NetworkCapabilities getNetworkCapabilities() {
-        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        boolean unmeteredApns = false;
-
-        if (mApnSetting != null && !mEnterpriseUse && !mMmsUseOnly) {
-            final int[] types = ApnSetting.getApnTypesFromBitmask(
-                    mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask);
-            for (int type : types) {
-                if ((!mRestrictedNetworkOverride && mUnmeteredUseOnly)
-                        && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
-                    log("Dropped the metered " + ApnSetting.getApnTypeString(type)
-                            + " type for the unmetered data call.");
-                    continue;
-                }
-                switch (type) {
-                    case ApnSetting.TYPE_ALL: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-                        break;
-                    }
-                    case ApnSetting.TYPE_DEFAULT: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-                        break;
-                    }
-                    case ApnSetting.TYPE_MMS: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
-                        break;
-                    }
-                    case ApnSetting.TYPE_SUPL: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
-                        break;
-                    }
-                    case ApnSetting.TYPE_DUN: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-                        break;
-                    }
-                    case ApnSetting.TYPE_FOTA: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
-                        break;
-                    }
-                    case ApnSetting.TYPE_IMS: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-                        break;
-                    }
-                    case ApnSetting.TYPE_CBS: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
-                        break;
-                    }
-                    case ApnSetting.TYPE_IA: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
-                        break;
-                    }
-                    case ApnSetting.TYPE_EMERGENCY: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
-                        break;
-                    }
-                    case ApnSetting.TYPE_MCX: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX);
-                        break;
-                    }
-                    case ApnSetting.TYPE_XCAP: {
-                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
-                        break;
-                    }
-                    default:
-                }
-            }
-
-            if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
-                unmeteredApns = true;
-            }
-        }
-
-        // Mark NOT_METERED in the following cases:
-        // 1. All APNs in the APN settings are unmetered.
-        // 2. The non-restricted data is intended for unmetered use only.
-        if (unmeteredApns || (mUnmeteredUseOnly && !mRestrictedNetworkOverride)) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        } else {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        }
-
-        if (mEnterpriseUse) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-        }
-
-        if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-        }
-
-        if (mMmsUseOnly) {
-            if (ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone)) {
-                log("Adding unmetered capability for the unmetered MMS-only data connection");
-                builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-            }
-            log("Adding MMS capability for the MMS-only data connection");
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
-        }
-
-        if (mRestrictedNetworkOverride) {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-            // don't use dun on restriction-overriden networks.
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-        }
-
-        builder.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth);
-        builder.setLinkUpstreamBandwidthKbps(mUplinkBandwidth);
-
-        builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
-                .setSubscriptionId(mSubId).build());
-        builder.setSubscriptionIds(Collections.singleton(mSubId));
-
-        if (!mPhone.getServiceState().getDataRoaming()) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
-        }
-
-        if (!mCongestedOverride) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
-        }
-
-        if (mUnmeteredOverride) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
-        }
-
-        if (!mIsSuspended) {
-            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
-        }
-
-        final int carrierServicePackageUid = getCarrierServicePackageUid();
-
-        // TODO(b/205736323): Owner and Admin UIDs currently come from separate data sources. Unify
-        //                    them, and remove ArrayUtils.contains() check.
-        if (carrierServicePackageUid != Process.INVALID_UID
-                && ArrayUtils.contains(mAdministratorUids, carrierServicePackageUid)) {
-            builder.setOwnerUid(carrierServicePackageUid);
-            builder.setAllowedUids(Collections.singleton(carrierServicePackageUid));
-        }
-        builder.setAdministratorUids(mAdministratorUids);
-
-        // Always start with NOT_VCN_MANAGED, then remove if VcnManager indicates this is part of a
-        // VCN.
-        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-        final VcnNetworkPolicyResult vcnPolicy = getVcnPolicy(builder.build());
-        if (!vcnPolicy.getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)) {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-        }
-        if (!vcnPolicy.getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-        }
-
-        return builder.build();
-    }
-
-    // TODO(b/205736323): Once TelephonyManager#getCarrierServicePackageNameForLogicalSlot() is
-    //                    plumbed to CarrierPrivilegesTracker's cache, query the cached UIDs.
-    private int getFirstUidForPackage(String pkgName) {
-        if (pkgName == null) {
-            return Process.INVALID_UID;
-        }
-
-        List<UserInfo> users = mPhone.getContext().getSystemService(UserManager.class).getUsers();
-        for (UserInfo user : users) {
-            int userId = user.getUserHandle().getIdentifier();
-            try {
-                PackageManager pm = mPhone.getContext().getPackageManager();
-
-                if (pm != null) {
-                    return pm.getPackageUidAsUser(pkgName, userId);
-                }
-            } catch (NameNotFoundException exception) {
-                // Didn't find package. Try other users
-                Rlog.i(
-                        "DataConnection",
-                        "Unable to find uid for package " + pkgName + " and user " + userId);
-            }
-        }
-        return Process.INVALID_UID;
-    }
-
-    private int getCarrierServicePackageUid() {
-        String pkgName =
-                mPhone.getContext()
-                        .getSystemService(TelephonyManager.class)
-                        .getCarrierServicePackageNameForLogicalSlot(mPhone.getPhoneId());
-
-        return getFirstUidForPackage(pkgName);
-    }
-
-    /**
-     * Check if the this data network is VCN-managed.
-     *
-     * @param networkCapabilities The network capabilities of this data network.
-     * @return The VCN's policy for this DataNetwork.
-     */
-    private VcnNetworkPolicyResult getVcnPolicy(NetworkCapabilities networkCapabilities) {
-        return mVcnManager.applyVcnNetworkPolicy(networkCapabilities, getLinkProperties());
-    }
-
-    /** @return {@code true} if validation is required, {@code false} otherwise. */
-    public boolean isValidationRequired() {
-        final NetworkCapabilities nc = getNetworkCapabilities();
-        return nc != null
-                && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-                && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-    }
-
-    /**
-     * @return {@code True} if 464xlat should be skipped.
-     */
-    @VisibleForTesting
-    public boolean shouldSkip464Xlat() {
-        switch (mApnSetting.getSkip464Xlat()) {
-            case Telephony.Carriers.SKIP_464XLAT_ENABLE:
-                return true;
-            case Telephony.Carriers.SKIP_464XLAT_DISABLE:
-                return false;
-            case Telephony.Carriers.SKIP_464XLAT_DEFAULT:
-            default:
-                break;
-        }
-
-        // As default, return true if ims and no internet
-        final NetworkCapabilities nc = getNetworkCapabilities();
-        return nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
-                && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-    }
-
-    /**
-     * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address.
-     */
-    @VisibleForTesting
-    public static boolean isIpAddress(String address) {
-        if (address == null) return false;
-
-        // Accept IPv6 addresses (only) in square brackets for compatibility.
-        if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) {
-            address = address.substring(1, address.length() - 1);
-        }
-        return InetAddresses.isNumericAddress(address);
-    }
-
-    private SetupResult setLinkProperties(DataCallResponse response,
-            LinkProperties linkProperties) {
-        // Check if system property dns usable
-        String propertyPrefix = "net." + response.getInterfaceName() + ".";
-        String dnsServers[] = new String[2];
-        dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
-        dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
-        boolean okToUseSystemPropertyDns = isDnsOk(dnsServers);
-
-        SetupResult result;
-
-        // Start with clean network properties and if we have
-        // a failure we'll clear again at the bottom of this code.
-        linkProperties.clear();
-
-        if (response.getCause() == DataFailCause.NONE) {
-            try {
-                // set interface name
-                linkProperties.setInterfaceName(response.getInterfaceName());
-
-                // set link addresses
-                if (response.getAddresses().size() > 0) {
-                    for (LinkAddress la : response.getAddresses()) {
-                        if (!la.getAddress().isAnyLocalAddress()) {
-                            if (DBG) {
-                                log("addr/pl=" + la.getAddress() + "/"
-                                        + la.getPrefixLength());
-                            }
-                            linkProperties.addLinkAddress(la);
-                        }
-                    }
-                } else {
-                    throw new UnknownHostException("no address for ifname="
-                            + response.getInterfaceName());
-                }
-
-                // set dns servers
-                if (response.getDnsAddresses().size() > 0) {
-                    for (InetAddress dns : response.getDnsAddresses()) {
-                        if (!dns.isAnyLocalAddress()) {
-                            linkProperties.addDnsServer(dns);
-                        }
-                    }
-                } else if (okToUseSystemPropertyDns) {
-                    for (String dnsAddr : dnsServers) {
-                        dnsAddr = dnsAddr.trim();
-                        if (dnsAddr.isEmpty()) continue;
-                        InetAddress ia;
-                        try {
-                            ia = InetAddresses.parseNumericAddress(dnsAddr);
-                        } catch (IllegalArgumentException e) {
-                            throw new UnknownHostException("Non-numeric dns addr=" + dnsAddr);
-                        }
-                        if (!ia.isAnyLocalAddress()) {
-                            linkProperties.addDnsServer(ia);
-                        }
-                    }
-                } else {
-                    throw new UnknownHostException("Empty dns response and no system default dns");
-                }
-
-                // set pcscf
-                if (response.getPcscfAddresses().size() > 0) {
-                    for (InetAddress pcscf : response.getPcscfAddresses()) {
-                        linkProperties.addPcscfServer(pcscf);
-                    }
-                }
-
-                for (InetAddress gateway : response.getGatewayAddresses()) {
-                    int mtu = gateway instanceof java.net.Inet6Address ? response.getMtuV6()
-                            : response.getMtuV4();
-                    // Allow 0.0.0.0 or :: as a gateway;
-                    // this indicates a point-to-point interface.
-                    linkProperties.addRoute(new RouteInfo(null, gateway, null,
-                            RouteInfo.RTN_UNICAST, mtu));
-                }
-
-                // set interface MTU
-                // this may clobber the setting read from the APN db, but that's ok
-                // TODO: remove once LinkProperties#setMtu is deprecated
-                linkProperties.setMtu(response.getMtu());
-
-                result = SetupResult.SUCCESS;
-            } catch (UnknownHostException e) {
-                log("setLinkProperties: UnknownHostException " + e);
-                result = SetupResult.ERROR_INVALID_ARG;
-            }
-        } else {
-            result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
-        }
-
-        // An error occurred so clear properties
-        if (result != SetupResult.SUCCESS) {
-            if (DBG) {
-                log("setLinkProperties: error clearing LinkProperties status="
-                        + response.getCause() + " result=" + result);
-            }
-            linkProperties.clear();
-        }
-
-        return result;
-    }
-
-    /**
-     * Initialize connection, this will fail if the
-     * apnSettings are not compatible.
-     *
-     * @param cp the Connection parameters
-     * @return true if initialization was successful.
-     */
-    private boolean initConnection(ConnectionParams cp) {
-        ApnContext apnContext = cp.mApnContext;
-        if (mApnSetting == null) {
-            // Only change apn setting if it isn't set, it will
-            // only NOT be set only if we're in DcInactiveState.
-            mApnSetting = apnContext.getApnSetting();
-        }
-        if (mApnSetting == null || (!mApnSetting.canHandleType(apnContext.getApnTypeBitmask())
-                && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE)) {
-            if (DBG) {
-                log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
-                        + " dc=" + DataConnection.this);
-            }
-            return false;
-        }
-        mTag += 1;
-        mConnectionParams = cp;
-        mConnectionParams.mTag = mTag;
-
-        // always update the ConnectionParams with the latest or the
-        // connectionGeneration gets stale
-        mApnContexts.put(apnContext, cp);
-
-        if (DBG) {
-            log("initConnection: "
-                    + " RefCount=" + mApnContexts.size()
-                    + " mApnList=" + mApnContexts
-                    + " mConnectionParams=" + mConnectionParams);
-        }
-        return true;
-    }
-
-    /**
-     * The parent state for all other states.
-     */
-    private class DcDefaultState extends State {
-        @Override
-        public void enter() {
-            if (DBG) log("DcDefaultState: enter");
-
-            // Register for DRS or RAT change
-            mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
-                    mTransportType, getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED, null);
-
-            mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null);
-            mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true);
-            mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
-                    DataConnection.EVENT_NR_STATE_CHANGED, null);
-            mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
-                    DataConnection.EVENT_NR_FREQUENCY_CHANGED, null);
-            mPhone.getServiceStateTracker().registerForCssIndicatorChanged(getHandler(),
-                    DataConnection.EVENT_CSS_INDICATOR_CHANGED, null);
-            if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
-                mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged(getHandler(),
-                        DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE, null);
-            }
-
-            // Add ourselves to the list of data connections
-            mDcController.addDc(DataConnection.this);
-        }
-        @Override
-        public void exit() {
-            if (DBG) log("DcDefaultState: exit");
-
-            // Unregister for DRS or RAT change.
-            mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
-                    mTransportType, getHandler());
-
-            mPhone.getServiceStateTracker().unregisterForDataRoamingOn(getHandler());
-            mPhone.getServiceStateTracker().unregisterForDataRoamingOff(getHandler());
-            mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
-            mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
-            mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
-            if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
-                mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler());
-            }
-
-            // Remove ourselves from the DC lists
-            mDcController.removeDc(DataConnection.this);
-
-            if (mAc != null) {
-                mAc.disconnected();
-                mAc = null;
-            }
-            mApnContexts.clear();
-            mReconnectIntent = null;
-            mDct = null;
-            mApnSetting = null;
-            mPhone = null;
-            mDataServiceManager = null;
-            mLinkProperties = null;
-            mLastFailCause = DataFailCause.NONE;
-            mUserData = null;
-            mDcController = null;
-            mDcTesterFailBringUpAll = null;
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            boolean retVal = HANDLED;
-
-            if (VDBG) {
-                log("DcDefault msg=" + getWhatToString(msg.what)
-                        + " RefCount=" + mApnContexts.size());
-            }
-            switch (msg.what) {
-                case EVENT_RESET:
-                    if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
-                    transitionTo(mInactiveState);
-                    break;
-                case EVENT_CONNECT:
-                    if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
-                    ConnectionParams cp = (ConnectionParams) msg.obj;
-                    notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
-                            DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
-                    break;
-
-                case EVENT_DISCONNECT:
-                case EVENT_DISCONNECT_ALL:
-                case EVENT_REEVALUATE_RESTRICTED_STATE:
-                    if (DBG) {
-                        log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what)
-                                + " RefCount=" + mApnContexts.size());
-                    }
-                    deferMessage(msg);
-                    break;
-                case EVENT_TEAR_DOWN_NOW:
-                    if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
-                    mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
-                            null);
-                    mDataCallSessionStats.setDeactivateDataCallReason(
-                            DataService.REQUEST_REASON_NORMAL);
-                    break;
-                case EVENT_LOST_CONNECTION:
-                    if (DBG) {
-                        String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
-                                + " tag=" + msg.arg1 + ":mTag=" + mTag;
-                        logAndAddLogRec(s);
-                    }
-                    break;
-                case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
-                    AsyncResult ar = (AsyncResult)msg.obj;
-                    Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result;
-                    mDataRegState = drsRatPair.first;
-                    updateTcpBufferSizes(drsRatPair.second);
-                    if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
-                        updateLinkBandwidthsFromCarrierConfig(drsRatPair.second);
-                    }
-                    mRilRat = drsRatPair.second;
-                    if (DBG) {
-                        log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
-                                + " regState=" + ServiceState.rilServiceStateToString(mDataRegState)
-                                + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat));
-                    }
-                    mDataCallSessionStats.onDrsOrRatChanged(
-                            ServiceState.rilRadioTechnologyToNetworkType(mRilRat));
-                    break;
-
-                case EVENT_START_HANDOVER:  //calls startHandover()
-                    if (DBG) {
-                        log("DcDefaultState: EVENT_START_HANDOVER not expected.");
-                    }
-                    Consumer<Integer> r = (Consumer<Integer>) msg.obj;
-                    r.accept(DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-                    break;
-                case EVENT_START_HANDOVER_ON_TARGET:
-                    if (DBG) {
-                        log("DcDefaultState: EVENT_START_HANDOVER not expected, but will "
-                                + "clean up, result code: "
-                                + DataServiceCallback.resultCodeToString(msg.arg1));
-                    }
-                    ((Consumer<Boolean>) msg.obj).accept(false /* is in correct state*/);
-                    break;
-                case EVENT_CANCEL_HANDOVER:
-                    // We don't need to do anything in this case
-                    if (DBG) {
-                        log("DcDefaultState: EVENT_CANCEL_HANDOVER resultCode="
-                                + DataServiceCallback.resultCodeToString(msg.arg1));
-                    }
-                    break;
-                case EVENT_RELEASE_PDU_SESSION_ID: {
-                    // We do the same thing in all state in order to preserve the existing workflow
-                    final AsyncResult asyncResult = (AsyncResult) msg.obj;
-                    if (asyncResult == null) {
-                        loge("EVENT_RELEASE_PDU_SESSION_ID: asyncResult is null!");
-                    } else {
-                        if (msg.obj != null) {
-                            if (DBG) logd("EVENT_RELEASE_PDU_SESSION_ID: id released");
-                            Runnable runnable = (Runnable) asyncResult.userObj;
-                            runnable.run();
-                        } else {
-                            loge("EVENT_RELEASE_PDU_SESSION_ID: no runnable set");
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_ALLOCATE_PDU_SESSION_ID: {
-                    // We do the same thing in all state in order to preserve the existing workflow
-                    final AsyncResult asyncResult = (AsyncResult) msg.obj;
-                    if (asyncResult == null) {
-                        loge("EVENT_ALLOCATE_PDU_SESSION_ID: asyncResult is null!");
-                    } else {
-                        Consumer<Integer> onAllocated = (Consumer<Integer>) asyncResult.userObj;
-                        if (asyncResult.exception != null) {
-                            loge("EVENT_ALLOCATE_PDU_SESSION_ID: exception",
-                                    asyncResult.exception);
-                            onAllocated.accept(PDU_SESSION_ID_NOT_SET);
-                        } else if (asyncResult.result == null) {
-                            loge("EVENT_ALLOCATE_PDU_SESSION_ID: result null, no id");
-                            onAllocated.accept(PDU_SESSION_ID_NOT_SET);
-                        } else {
-                            int psi = (int) asyncResult.result;
-                            if (DBG) logd("EVENT_ALLOCATE_PDU_SESSION_ID: psi=" + psi);
-                            onAllocated.accept(psi);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                default:
-                    if (DBG) {
-                        log("DcDefaultState: ignore msg.what=" + getWhatToString(msg.what));
-                    }
-                    break;
-            }
-
-            return retVal;
-        }
-    }
-
-    private void updateSuspendState() {
-        if (mNetworkAgent == null) {
-            Rlog.d(getName(), "Setting suspend state without a NetworkAgent");
-        }
-
-        boolean newSuspendedState = false;
-        // Data can only be (temporarily) suspended while data is in active state
-        if (getCurrentState() == mActiveState) {
-            // Never set suspended for emergency apn. Emergency data connection
-            // can work while device is not in service.
-            if (mApnSetting != null && mApnSetting.isEmergencyApn()) {
-                newSuspendedState = false;
-            // If we are not in service, change to suspended.
-            } else if (mDataRegState != ServiceState.STATE_IN_SERVICE) {
-                newSuspendedState = true;
-            // Check voice/data concurrency.
-            } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-                newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE;
-            }
-        }
-
-        // Only notify when there is a change.
-        if (mIsSuspended != newSuspendedState) {
-            mIsSuspended = newSuspendedState;
-
-            // If data connection is active, we need to notify the new data connection state
-            // changed event reflecting the latest suspended state.
-            if (isActive()) {
-                notifyDataConnectionState();
-            }
-        }
-    }
-
-    private void notifyDataConnectionState() {
-        // The receivers of this have no way to differentiate between default and enterprise
-        // connections. Do not notify for enterprise.
-        if (!isEnterpriseUse()) {
-            mPhone.notifyDataConnection(getPreciseDataConnectionState());
-        } else {
-            log("notifyDataConnectionState: Skipping for enterprise; state=" + getState());
-        }
-    }
-
-    private DcDefaultState mDefaultState = new DcDefaultState();
-
-    private int getApnTypeBitmask() {
-        return isEnterpriseUse() ? ApnSetting.TYPE_ENTERPRISE :
-                mApnSetting != null ? mApnSetting.getApnTypeBitmask() : 0;
-    }
-
-    private boolean canHandleDefault() {
-        return !isEnterpriseUse() && mApnSetting != null
-                ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false;
-    }
-
-    /**
-     * The state machine is inactive and expects a EVENT_CONNECT.
-     */
-    private class DcInactiveState extends State {
-        // Inform all contexts we've failed connecting
-        public void setEnterNotificationParams(ConnectionParams cp, @DataFailureCause int cause,
-                @HandoverFailureMode int handoverFailureMode) {
-            if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause");
-            mConnectionParams = cp;
-            mDisconnectParams = null;
-            mDcFailCause = cause;
-            mHandoverFailureMode = handoverFailureMode;
-        }
-
-        // Inform all contexts we've failed disconnected
-        public void setEnterNotificationParams(DisconnectParams dp) {
-            if (VDBG) log("DcInactiveState: setEnterNotificationParams dp");
-            mConnectionParams = null;
-            mDisconnectParams = dp;
-            mDcFailCause = DataFailCause.NONE;
-        }
-
-        // Inform all contexts of the failure cause
-        public void setEnterNotificationParams(@DataFailureCause int cause) {
-            mConnectionParams = null;
-            mDisconnectParams = null;
-            mDcFailCause = cause;
-        }
-
-        @Override
-        public void enter() {
-            mTag += 1;
-            if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
-            TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
-                    TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE,
-                    mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
-            mDataCallSessionStats.onDataCallDisconnected(mDcFailCause);
-            if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) {
-                // This is from source data connection to set itself's state
-                setHandoverState(HANDOVER_STATE_COMPLETED);
-            }
-
-            // Check for dangling agent. Ideally the handover source agent should be null if
-            // handover process is smooth. When it's not null, that means handover failed. The
-            // agent was not successfully transferred to the new data connection. We should
-            // gracefully notify connectivity service the network was disconnected.
-            if (mHandoverSourceNetworkAgent != null) {
-                DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection();
-                if (sourceDc != null) {
-                    // If the source data connection still owns this agent, then just reset the
-                    // handover state back to idle because handover is already failed.
-                    mHandoverLocalLog.log(
-                            "Handover failed. Reset the source dc " + sourceDc.getName()
-                                    + " state to idle");
-                    sourceDc.cancelHandover();
-                } else {
-                    // The agent is now a dangling agent. No data connection owns this agent.
-                    // Gracefully notify connectivity service disconnected.
-                    mHandoverLocalLog.log(
-                            "Handover failed and dangling agent found.");
-                    mHandoverSourceNetworkAgent.acquireOwnership(
-                            DataConnection.this, mTransportType);
-                    log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent);
-                    mHandoverSourceNetworkAgent.unregister(DataConnection.this);
-                    mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this);
-                }
-                mHandoverSourceNetworkAgent = null;
-            }
-
-            if (mConnectionParams != null) {
-                if (DBG) {
-                    log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
-                            + DataFailCause.toString(mDcFailCause));
-                }
-                notifyConnectCompleted(mConnectionParams, mDcFailCause, mHandoverFailureMode,
-                        true);
-            }
-            if (mDisconnectParams != null) {
-                if (DBG) {
-                    log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause="
-                            + DataFailCause.toString(mDcFailCause));
-                }
-                notifyDisconnectCompleted(mDisconnectParams, true);
-            }
-            if (mDisconnectParams == null && mConnectionParams == null
-                    && mDcFailCause != DataFailCause.NONE) {
-                if (DBG) {
-                    log("DcInactiveState: enter notifyAllDisconnectCompleted failCause="
-                            + DataFailCause.toString(mDcFailCause));
-                }
-                notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE,
-                        DataFailCause.toString(mDcFailCause));
-            }
-
-            // Remove ourselves from cid mapping, before clearSettings
-            mDcController.removeActiveDcByCid(DataConnection.this);
-
-            // For the first time entering here (idle state before setup), do not notify
-            // disconnected state. Only notify data connection disconnected for data that is
-            // actually moving from disconnecting to disconnected, or setup failed. In both cases,
-            // APN setting will not be null.
-            if (mApnSetting != null) {
-                notifyDataConnectionState();
-            }
-            clearSettings();
-        }
-
-        @Override
-        public void exit() {
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_RESET:
-                case EVENT_REEVALUATE_RESTRICTED_STATE:
-                    if (DBG) {
-                        log("DcInactiveState: msg.what=" + getWhatToString(msg.what)
-                                + ", ignore we're already done");
-                    }
-                    return HANDLED;
-                case EVENT_CONNECT:
-                    if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
-                    ConnectionParams cp = (ConnectionParams) msg.obj;
-
-                    if (!initConnection(cp)) {
-                        log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
-                        notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
-                                DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
-                        transitionTo(mInactiveState);
-                        return HANDLED;
-                    }
-
-                    int cause = connect(cp);
-                    if (cause != DataFailCause.NONE) {
-                        log("DcInactiveState: msg.what=EVENT_CONNECT connect failed");
-                        notifyConnectCompleted(cp, cause,
-                                DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
-                        transitionTo(mInactiveState);
-                        return HANDLED;
-                    }
-
-                    if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                        mSubId = cp.mSubId;
-                    }
-
-                    transitionTo(mActivatingState);
-                    return HANDLED;
-                case EVENT_DISCONNECT:
-                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
-                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
-                    return HANDLED;
-                case EVENT_DISCONNECT_ALL:
-                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
-                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
-                    return HANDLED;
-                default:
-                    if (VDBG) {
-                        log("DcInactiveState not handled msg.what=" + getWhatToString(msg.what));
-                    }
-                    return NOT_HANDLED;
-            }
-        }
-    }
-    private DcInactiveState mInactiveState = new DcInactiveState();
-
-    /**
-     * The state machine is activating a connection.
-     */
-    private class DcActivatingState extends State {
-        @Override
-        public void enter() {
-            int apnTypeBitmask = getApnTypeBitmask();
-            TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
-                    TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING,
-                    mPhone.getPhoneId(), mId, apnTypeBitmask, canHandleDefault());
-            setHandoverState(HANDOVER_STATE_IDLE);
-            // restricted evaluation depends on network requests from apnContext. The evaluation
-            // should happen once entering connecting state rather than active state because it's
-            // possible that restricted network request can be released during the connecting window
-            // and if we wait for connection established, then we might mistakenly
-            // consider it as un-restricted. ConnectivityService then will immediately
-            // tear down the connection through networkAgent unwanted callback if all requests for
-            // this connection are going away.
-            mRestrictedNetworkOverride = shouldRestrictNetwork();
-
-            CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker();
-            if (carrierPrivTracker != null) {
-                carrierPrivTracker.registerCarrierPrivilegesListener(
-                            getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null);
-            }
-            notifyDataConnectionState();
-            mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
-        }
-        @Override
-        public boolean processMessage(Message msg) {
-            boolean retVal;
-            AsyncResult ar;
-            ConnectionParams cp;
-
-            if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
-            switch (msg.what) {
-                case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
-                case EVENT_CONNECT:
-                    // Activating can't process until we're done.
-                    deferMessage(msg);
-                    retVal = HANDLED;
-                    break;
-
-                case EVENT_SETUP_DATA_CONNECTION_DONE:
-                    cp = (ConnectionParams) msg.obj;
-
-                    DataCallResponse dataCallResponse =
-                            msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
-                    SetupResult result = onSetupConnectionCompleted(msg.arg1, dataCallResponse, cp);
-                    if (result != SetupResult.ERROR_STALE) {
-                        if (mConnectionParams != cp) {
-                            loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
-                                    + " != cp:" + cp);
-                        }
-                    }
-                    if (DBG) {
-                        log("DcActivatingState onSetupConnectionCompleted result=" + result
-                                + " dc=" + DataConnection.this);
-                    }
-                    ApnContext.requestLog(
-                            cp.mApnContext, "onSetupConnectionCompleted result=" + result);
-
-                    if (result != SetupResult.SUCCESS) {
-                        releasePduSessionId(() -> DataConnection.this
-                                .setPduSessionId(PDU_SESSION_ID_NOT_SET));
-                    }
-
-                    switch (result) {
-                        case SUCCESS:
-                            // All is well
-                            mDcFailCause = DataFailCause.NONE;
-                            transitionTo(mActiveState);
-                            break;
-                        case ERROR_RADIO_NOT_AVAILABLE:
-                            // Vendor ril rejected the command and didn't connect.
-                            // Transition to inactive but send notifications after
-                            // we've entered the mInactive state.
-                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
-                                    DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
-                            transitionTo(mInactiveState);
-                            break;
-                        case ERROR_DUPLICATE_CID:
-                            // TODO (b/180988471): Properly handle the case when an existing cid is
-                            // returned by tearing down the network agent if enterprise changed.
-                            long retry = RetryManager.NO_SUGGESTED_RETRY_DELAY;
-                            if (cp.mApnContext != null) {
-                                retry = RetryManager.NO_RETRY;
-                                mDct.getDataThrottler().setRetryTime(
-                                        cp.mApnContext.getApnTypeBitmask(),
-                                        retry, DcTracker.REQUEST_TYPE_NORMAL);
-                            }
-                            String logStr = "DcActivatingState: "
-                                    + DataFailCause.toString(result.mFailCause)
-                                    + " retry=" + retry;
-                            if (DBG) log(logStr);
-                            ApnContext.requestLog(cp.mApnContext, logStr);
-                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
-                                    DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
-                            transitionTo(mInactiveState);
-                            break;
-                        case ERROR_NO_DEFAULT_CONNECTION:
-                            // TODO (b/180988471): Properly handle the case when a default data
-                            // connection doesn't exist (tear down connection and retry).
-                            // Currently, this just tears down the connection without retry.
-                            if (DBG) log("DcActivatingState: NO_DEFAULT_DATA");
-                        case ERROR_INVALID_ARG:
-                            // The addresses given from the RIL are bad
-                            tearDownData(cp);
-                            transitionTo(mDisconnectingErrorCreatingConnection);
-                            break;
-                        case ERROR_DATA_SERVICE_SPECIFIC_ERROR:
-
-                            // Retrieve the suggested retry delay from the modem and save it.
-                            // If the modem want us to retry the current APN again, it will
-                            // suggest a positive delay value (in milliseconds). Otherwise we'll get
-                            // NO_SUGGESTED_RETRY_DELAY here.
-
-                            long delay = getSuggestedRetryDelay(dataCallResponse);
-                            long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
-                            if (delay == RetryManager.NO_RETRY) {
-                                retryTime = RetryManager.NO_RETRY;
-                            } else if (delay >= 0) {
-                                retryTime = SystemClock.elapsedRealtime() + delay;
-                            }
-                            int newRequestType = DcTracker.calculateNewRetryRequestType(
-                                    mHandoverFailureMode, cp.mRequestType, mDcFailCause);
-                            mDct.getDataThrottler().setRetryTime(getApnTypeBitmask(),
-                                    retryTime, newRequestType);
-
-                            String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
-                                    + " delay=" + delay
-                                    + " result=" + result
-                                    + " result.isRadioRestartFailure="
-                                    + DataFailCause.isRadioRestartFailure(mPhone.getContext(),
-                                    result.mFailCause, mPhone.getSubId())
-                                    + " isPermanentFailure=" +
-                                    mDct.isPermanentFailure(result.mFailCause);
-                            if (DBG) log(str);
-                            ApnContext.requestLog(cp.mApnContext, str);
-
-                            // Save the cause. DcTracker.onDataSetupComplete will check this
-                            // failure cause and determine if we need to retry this APN later
-                            // or not.
-                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
-                                    dataCallResponse != null
-                                            ? dataCallResponse.getHandoverFailureMode()
-                                            : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
-                            transitionTo(mInactiveState);
-                            break;
-                        case ERROR_STALE:
-                            loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
-                                    + " tag:" + cp.mTag + " != mTag:" + mTag);
-                            break;
-                        default:
-                            throw new RuntimeException("Unknown SetupResult, should not happen");
-                    }
-                    retVal = HANDLED;
-                    mDataCallSessionStats
-                            .onSetupDataCallResponse(dataCallResponse,
-                                    ServiceState.rilRadioTechnologyToNetworkType(cp.mRilRat),
-                                    getApnTypeBitmask(), mApnSetting.getProtocol(),
-                                    result.mFailCause);
-                    break;
-                case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
-                    AsyncResult asyncResult = (AsyncResult) msg.obj;
-                    int[] administratorUids = (int[]) asyncResult.result;
-                    mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
-                    retVal = HANDLED;
-                    break;
-                case EVENT_START_HANDOVER_ON_TARGET:
-                    //called after startHandover on target transport
-                    ((Consumer<Boolean>) msg.obj).accept(true /* is in correct state*/);
-                    retVal = HANDLED;
-                    break;
-                case EVENT_CANCEL_HANDOVER:
-                    transitionTo(mInactiveState);
-                    retVal = HANDLED;
-                    break;
-                default:
-                    if (VDBG) {
-                        log("DcActivatingState not handled msg.what=" +
-                                getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
-                    }
-                    retVal = NOT_HANDLED;
-                    break;
-            }
-            return retVal;
-        }
-    }
-    private DcActivatingState mActivatingState = new DcActivatingState();
-
-    /**
-     * The state machine is connected, expecting an EVENT_DISCONNECT.
-     */
-    private class DcActiveState extends State {
-
-        @Override public void enter() {
-            if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
-            TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
-                    TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE,
-                    mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
-            // If we were retrying there maybe more than one, otherwise they'll only be one.
-            notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE,
-                    Phone.REASON_CONNECTED);
-
-            mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
-            mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null);
-
-            // If the EVENT_CONNECT set the current max retry restore it here
-            // if it didn't then this is effectively a NOP.
-            mDcController.addActiveDcByCid(DataConnection.this);
-
-            updateTcpBufferSizes(mRilRat);
-            updateLinkBandwidthsFromCarrierConfig(mRilRat);
-
-            final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
-            configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
-            configBuilder.setLegacyTypeName(NETWORK_TYPE);
-            int networkType = getNetworkType();
-            configBuilder.setLegacySubType(networkType);
-            configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType));
-            configBuilder.setLegacyExtraInfo(mApnSetting.getApnName());
-            final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent();
-            if (carrierSignalAgent.hasRegisteredReceivers(TelephonyManager
-                    .ACTION_CARRIER_SIGNAL_REDIRECTED)) {
-                // carrierSignal Receivers will place the carrier-specific provisioning notification
-                configBuilder.setProvisioningNotificationEnabled(false);
-            }
-
-            final String subscriberId = mPhone.getSubscriberId();
-            if (!TextUtils.isEmpty(subscriberId)) {
-                configBuilder.setSubscriberId(subscriberId);
-            }
-
-            // set skip464xlat if it is not default otherwise
-            if (shouldSkip464Xlat()) {
-                configBuilder.setNat64DetectionEnabled(false);
-            }
-
-            mUnmeteredUseOnly = isUnmeteredUseOnly();
-            mMmsUseOnly = isMmsUseOnly();
-            mEnterpriseUse = isEnterpriseUse();
-
-            if (DBG) {
-                log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride
-                        + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly
-                        + ", mMmsUseOnly = " + mMmsUseOnly
-                        + ", mEnterpriseUse = " + mEnterpriseUse);
-            }
-
-            // Always register a VcnNetworkPolicyChangeListener, regardless of whether this is a
-            // handover
-            // or new Network.
-            mVcnManager.addVcnNetworkPolicyChangeListener(
-                    new HandlerExecutor(getHandler()), mVcnPolicyChangeListener);
-
-            if (mConnectionParams != null
-                    && mConnectionParams.mRequestType == REQUEST_TYPE_HANDOVER) {
-                // If this is a data setup for handover, we need to reuse the existing network agent
-                // instead of creating a new one. This should be transparent to connectivity
-                // service.
-                DcTracker dcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
-                DataConnection dc = dcTracker.getDataConnectionByApnType(
-                        mConnectionParams.mApnContext.getApnType());
-                // It's possible that the source data connection has been disconnected by the modem
-                // already. If not, set its handover state to completed.
-                if (dc != null) {
-                    // Transfer network agent from the original data connection as soon as the
-                    // new handover data connection is connected.
-                    dc.setHandoverState(HANDOVER_STATE_COMPLETED);
-                }
-
-                if (mHandoverSourceNetworkAgent != null) {
-                    String logStr = "Transfer network agent " + mHandoverSourceNetworkAgent.getTag()
-                            + " successfully.";
-                    log(logStr);
-                    mHandoverLocalLog.log(logStr);
-                    mNetworkAgent = mHandoverSourceNetworkAgent;
-                    mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType);
-
-                    // TODO: Should evaluate mDisabledApnTypeBitMask again after handover. We don't
-                    // do it now because connectivity service does not support dynamically removing
-                    // immutable capabilities.
-
-                    mNetworkAgent.updateLegacySubtype(DataConnection.this);
-                    // Update the capability after handover
-                    mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                            DataConnection.this);
-                    mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
-                    mHandoverSourceNetworkAgent = null;
-                } else {
-                    String logStr = "Failed to get network agent from original data connection";
-                    loge(logStr);
-                    mHandoverLocalLog.log(logStr);
-                    return;
-                }
-            } else {
-                mScore = calculateScore();
-                final NetworkFactory factory = PhoneFactory.getNetworkFactory(
-                        mPhone.getPhoneId());
-                final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
-
-                mDisabledApnTypeBitMask |= getDisallowedApnTypes();
-                updateLinkPropertiesHttpProxy();
-                mNetworkAgent = new DcNetworkAgent(DataConnection.this, mPhone, mScore,
-                        configBuilder.build(), provider, mTransportType);
-
-                VcnNetworkPolicyResult policyResult =
-                        mVcnManager.applyVcnNetworkPolicy(
-                                getNetworkCapabilities(), getLinkProperties());
-                if (policyResult.isTeardownRequested()) {
-                    tearDownAll(
-                            Phone.REASON_VCN_REQUESTED_TEARDOWN,
-                            DcTracker.RELEASE_TYPE_DETACH,
-                            null /* onCompletedMsg */);
-                } else {
-                    // All network agents start out in CONNECTING mode, but DcNetworkAgents are
-                    // created when the network is already connected. Hence, send the connected
-                    // notification immediately.
-                    mNetworkAgent.markConnected();
-                }
-
-                // The network agent is always created with NOT_SUSPENDED capability, but the
-                // network might be already out of service (or voice call is ongoing) just right
-                // before data connection is created. Connectivity service would not allow a network
-                // created with suspended state, so we create a non-suspended network first, and
-                // then immediately evaluate the suspended state.
-                sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE));
-            }
-
-            // The qos parameters are set when the call is connected
-            syncQosToNetworkAgent();
-
-            if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                mPhone.mCi.registerForNattKeepaliveStatus(
-                        getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
-                mPhone.mCi.registerForLceInfo(
-                        getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
-            }
-            notifyDataConnectionState();
-            TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
-                    mCid, getApnTypeBitmask(), RilDataCall.State.CONNECTED);
-        }
-
-        @Override
-        public void exit() {
-            if (DBG) log("DcActiveState: exit dc=" + this);
-            mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
-            mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
-
-            if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
-                mPhone.mCi.unregisterForLceInfo(getHandler());
-            }
-
-            // If we are still owning this agent, then we should inform connectivity service the
-            // data connection is disconnected. There is one exception that we shouldn't unregister,
-            // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will
-            // be transferred to the new data connection on the other transport.
-            if (mNetworkAgent != null) {
-                syncQosToNetworkAgent();
-                if (mHandoverState == HANDOVER_STATE_IDLE) {
-                    mNetworkAgent.unregister(DataConnection.this);
-                }
-                mNetworkAgent.releaseOwnership(DataConnection.this);
-            }
-            mNetworkAgent = null;
-
-            TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
-                    mCid, getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
-
-            mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener);
-
-            CarrierPrivilegesTracker carrierPrivTracker = mPhone.getCarrierPrivilegesTracker();
-            if (carrierPrivTracker != null) {
-                carrierPrivTracker.unregisterCarrierPrivilegesListener(getHandler());
-            }
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            boolean retVal;
-
-            switch (msg.what) {
-                case EVENT_CONNECT: {
-                    ConnectionParams cp = (ConnectionParams) msg.obj;
-                    // either add this new apn context to our set or
-                    // update the existing cp with the latest connection generation number
-                    mApnContexts.put(cp.mApnContext, cp);
-                    // TODO (b/118347948): evaluate if it's still needed after assigning
-                    // different scores to different Cellular network.
-                    mDisabledApnTypeBitMask &= ~cp.mApnContext.getApnTypeBitmask();
-                    mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                            DataConnection.this);
-                    if (DBG) {
-                        log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
-                    }
-                    notifyConnectCompleted(cp, DataFailCause.NONE,
-                            DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_DISCONNECT: {
-                    DisconnectParams dp = (DisconnectParams) msg.obj;
-                    if (DBG) {
-                        log("DcActiveState: EVENT_DISCONNECT dp=" + dp
-                                + " dc=" + DataConnection.this);
-                    }
-                    if (mApnContexts.containsKey(dp.mApnContext)) {
-                        if (DBG) {
-                            log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
-                                    + mApnContexts.size());
-                        }
-
-                        if (mApnContexts.size() == 1) {
-                            mApnContexts.clear();
-                            mDisconnectParams = dp;
-                            mConnectionParams = null;
-                            dp.mTag = mTag;
-                            tearDownData(dp);
-                            transitionTo(mDisconnectingState);
-                        } else {
-                            mApnContexts.remove(dp.mApnContext);
-                            // TODO (b/118347948): evaluate if it's still needed after assigning
-                            // different scores to different Cellular network.
-                            mDisabledApnTypeBitMask |= dp.mApnContext.getApnTypeBitmask();
-                            mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                    DataConnection.this);
-                            notifyDisconnectCompleted(dp, false);
-                        }
-                    } else {
-                        log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
-                                + " in this dc=" + DataConnection.this);
-                        notifyDisconnectCompleted(dp, false);
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_DISCONNECT_ALL: {
-                    if (DBG) {
-                        log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
-                                + " dc=" + DataConnection.this);
-                    }
-                    DisconnectParams dp = (DisconnectParams) msg.obj;
-                    mDisconnectParams = dp;
-                    mConnectionParams = null;
-                    dp.mTag = mTag;
-                    tearDownData(dp);
-                    transitionTo(mDisconnectingState);
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_LOST_CONNECTION: {
-                    if (DBG) {
-                        log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
-                    }
-
-                    mInactiveState.setEnterNotificationParams(DataFailCause.LOST_CONNECTION);
-                    transitionTo(mInactiveState);
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result;
-                    mDataRegState = drsRatPair.first;
-                    updateTcpBufferSizes(drsRatPair.second);
-                    if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
-                        updateLinkBandwidthsFromCarrierConfig(drsRatPair.second);
-                    }
-                    mRilRat = drsRatPair.second;
-                    if (DBG) {
-                        log("DcActiveState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
-                                + " drs=" + mDataRegState
-                                + " mRilRat=" + mRilRat);
-                    }
-                    updateSuspendState();
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.updateLegacySubtype(DataConnection.this);
-                        // The new suspended state will be passed through connectivity service
-                        // through NET_CAPABILITY_NOT_SUSPENDED.
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                        mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    mDataCallSessionStats.onDrsOrRatChanged(
-                            ServiceState.rilRadioTechnologyToNetworkType(mRilRat));
-                    break;
-                }
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // fallthrough
-                case EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED:
-                    if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
-                        updateLinkBandwidthsFromCarrierConfig(mRilRat);
-                    }
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED:
-                    boolean isUnmetered = (boolean) msg.obj;
-                    if (isUnmetered == mUnmeteredOverride) {
-                        retVal = HANDLED;
-                        break;
-                    }
-                    mUnmeteredOverride = isUnmetered;
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.updateLegacySubtype(DataConnection.this);
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                case EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED:
-                    boolean isCongested = (boolean) msg.obj;
-                    if (isCongested == mCongestedOverride) {
-                        retVal = HANDLED;
-                        break;
-                    }
-                    mCongestedOverride = isCongested;
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.updateLegacySubtype(DataConnection.this);
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                case EVENT_DATA_CONNECTION_ROAM_ON:
-                case EVENT_DATA_CONNECTION_ROAM_OFF: {
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.updateLegacySubtype(DataConnection.this);
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
-                case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED:
-                case EVENT_CSS_INDICATOR_CHANGED:
-                case EVENT_UPDATE_SUSPENDED_STATE: {
-                    updateSuspendState();
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_BW_REFRESH_RESPONSE: {
-                    AsyncResult ar = (AsyncResult)msg.obj;
-                    if (ar.exception != null) {
-                        log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
-                    } else {
-                        if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
-                            updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_KEEPALIVE_START_REQUEST: {
-                    KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
-                    int slotId = msg.arg1;
-                    int intervalMillis = msg.arg2 * 1000;
-                    if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                        mPhone.mCi.startNattKeepalive(
-                                DataConnection.this.mCid, pkt, intervalMillis,
-                                DataConnection.this.obtainMessage(
-                                        EVENT_KEEPALIVE_STARTED, slotId, 0, null));
-                    } else {
-                        // We currently do not support NATT Keepalive requests using the
-                        // DataService API, so unless the request is WWAN (always bound via
-                        // the CommandsInterface), the request cannot be honored.
-                        //
-                        // TODO: b/72331356 to add support for Keepalive to the DataService
-                        // so that keepalive requests can be handled (if supported) by the
-                        // underlying transport.
-                        if (mNetworkAgent != null) {
-                            mNetworkAgent.sendSocketKeepaliveEvent(
-                                    msg.arg1, SocketKeepalive.ERROR_INVALID_NETWORK);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_KEEPALIVE_STOP_REQUEST: {
-                    int slotId = msg.arg1;
-                    int handle = mNetworkAgent.keepaliveTracker.getHandleForSlot(slotId);
-                    if (handle < 0) {
-                        loge("No slot found for stopSocketKeepalive! " + slotId);
-                        mNetworkAgent.sendSocketKeepaliveEvent(
-                                slotId, SocketKeepalive.ERROR_NO_SUCH_SLOT);
-                        retVal = HANDLED;
-                        break;
-                    } else {
-                        logd("Stopping keepalive with handle: " + handle);
-                    }
-
-                    mPhone.mCi.stopNattKeepalive(
-                            handle, DataConnection.this.obtainMessage(
-                                    EVENT_KEEPALIVE_STOPPED, handle, slotId, null));
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_KEEPALIVE_STARTED: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    final int slot = msg.arg1;
-                    if (ar.exception != null || ar.result == null) {
-                        loge("EVENT_KEEPALIVE_STARTED: error starting keepalive, e="
-                                + ar.exception);
-                        mNetworkAgent.sendSocketKeepaliveEvent(
-                                slot, SocketKeepalive.ERROR_HARDWARE_ERROR);
-                    } else {
-                        KeepaliveStatus ks = (KeepaliveStatus) ar.result;
-                        if (ks == null) {
-                            loge("Null KeepaliveStatus received!");
-                        } else {
-                            mNetworkAgent.keepaliveTracker.handleKeepaliveStarted(slot, ks);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_KEEPALIVE_STATUS: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        loge("EVENT_KEEPALIVE_STATUS: error in keepalive, e=" + ar.exception);
-                        // We have no way to notify connectivity in this case.
-                    }
-                    if (ar.result != null) {
-                        KeepaliveStatus ks = (KeepaliveStatus) ar.result;
-                        mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(ks);
-                    }
-
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_KEEPALIVE_STOPPED: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    final int handle = msg.arg1;
-                    final int slotId = msg.arg2;
-
-                    if (ar.exception != null) {
-                        loge("EVENT_KEEPALIVE_STOPPED: error stopping keepalive for handle="
-                                + handle + " e=" + ar.exception);
-                        mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(
-                                new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN));
-                    } else {
-                        log("Keepalive Stop Requested for handle=" + handle);
-                        mNetworkAgent.keepaliveTracker.handleKeepaliveStatus(
-                                new KeepaliveStatus(
-                                        handle, KeepaliveStatus.STATUS_INACTIVE));
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_LINK_CAPACITY_CHANGED: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        loge("EVENT_LINK_CAPACITY_CHANGED e=" + ar.exception);
-                    } else {
-                        if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
-                            updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: {
-                    AsyncResult ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        loge("EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE e=" + ar.exception);
-                    } else {
-                        Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result;
-                        if (isBandwidthSourceKey(
-                                DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
-                            updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_REEVALUATE_RESTRICTED_STATE: {
-                    // If the network was restricted, and now it does not need to be restricted
-                    // anymore, we should add the NET_CAPABILITY_NOT_RESTRICTED capability.
-                    if (mRestrictedNetworkOverride && !shouldRestrictNetwork()) {
-                        if (DBG) {
-                            log("Data connection becomes not-restricted. dc=" + this);
-                        }
-                        // Note we only do this when network becomes non-restricted. When a
-                        // non-restricted becomes restricted (e.g. users disable data, or turn off
-                        // data roaming), DCT will explicitly tear down the networks (because
-                        // connectivity service does not support force-close TCP connections today).
-                        // Also note that NET_CAPABILITY_NOT_RESTRICTED is an immutable capability
-                        // (see {@link NetworkCapabilities}) once we add it to the network, we can't
-                        // remove it through the entire life cycle of the connection.
-                        mRestrictedNetworkOverride = false;
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-
-                    // If the data does need to be unmetered use only (e.g. users turn on data, or
-                    // device is not roaming anymore assuming data roaming is off), then we can
-                    // dynamically add those metered APN type capabilities back. (But not the
-                    // other way around because most of the APN-type capabilities are immutable
-                    // capabilities.)
-                    if (mUnmeteredUseOnly && !isUnmeteredUseOnly()) {
-                        mUnmeteredUseOnly = false;
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-
-                    mMmsUseOnly = isMmsUseOnly();
-
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES: {
-                    // Update other properties like link properties if needed in future.
-                    updateScore();
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_NR_STATE_CHANGED: {
-                    updateTcpBufferSizes(mRilRat);
-                    if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY)) {
-                        updateLinkBandwidthsFromCarrierConfig(mRilRat);
-                    }
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                }
-                case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
-                    AsyncResult asyncResult = (AsyncResult) msg.obj;
-                    int[] administratorUids = (int[]) asyncResult.result;
-                    mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
-
-                    // Administrator UIDs changed, so update NetworkAgent with new
-                    // NetworkCapabilities
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendNetworkCapabilities(
-                                getNetworkCapabilities(), DataConnection.this);
-                    }
-                    retVal = HANDLED;
-                    break;
-                case EVENT_START_HANDOVER:  //calls startHandover()
-                    Consumer<Integer> r = (Consumer<Integer>) msg.obj;
-                    r.accept(msg.arg1);
-                    retVal = HANDLED;
-                    break;
-
-                default:
-                    if (VDBG) {
-                        log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
-                    }
-                    retVal = NOT_HANDLED;
-                    break;
-            }
-            return retVal;
-        }
-    }
-    private DcActiveState mActiveState = new DcActiveState();
-
-    /**
-     * The state machine is disconnecting.
-     */
-    private class DcDisconnectingState extends State {
-        @Override
-        public void enter() {
-            TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
-                    TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING,
-                    mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
-            notifyDataConnectionState();
-        }
-        @Override
-        public boolean processMessage(Message msg) {
-            boolean retVal;
-
-            switch (msg.what) {
-                case EVENT_CONNECT:
-                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
-                            + mApnContexts.size());
-                    deferMessage(msg);
-                    retVal = HANDLED;
-                    break;
-
-                case EVENT_DEACTIVATE_DONE:
-                    DisconnectParams dp = (DisconnectParams) msg.obj;
-
-                    String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
-                            + mApnContexts.size();
-
-                    if (DBG) log(str);
-                    ApnContext.requestLog(dp.mApnContext, str);
-
-                    // Clear out existing qos sessions
-                    updateQosParameters(null);
-
-                    if (dp.mTag == mTag) {
-                        // Transition to inactive but send notifications after
-                        // we've entered the mInactive state.
-                        mInactiveState.setEnterNotificationParams(dp);
-                        transitionTo(mInactiveState);
-                    } else {
-                        if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
-                                + " dp.tag=" + dp.mTag + " mTag=" + mTag);
-                    }
-                    retVal = HANDLED;
-                    break;
-
-                default:
-                    if (VDBG) {
-                        log("DcDisconnectingState not handled msg.what="
-                                + getWhatToString(msg.what));
-                    }
-                    retVal = NOT_HANDLED;
-                    break;
-            }
-            return retVal;
-        }
-    }
-    private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
-
-    /**
-     * The state machine is disconnecting after an creating a connection.
-     */
-    private class DcDisconnectionErrorCreatingConnection extends State {
-        @Override
-        public void enter() {
-            TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
-                    TelephonyStatsLog
-                            .MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION,
-                    mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
-            notifyDataConnectionState();
-        }
-        @Override
-        public boolean processMessage(Message msg) {
-            boolean retVal;
-
-            switch (msg.what) {
-                case EVENT_DEACTIVATE_DONE:
-                    ConnectionParams cp = (ConnectionParams) msg.obj;
-                    if (cp.mTag == mTag) {
-                        String str = "DcDisconnectionErrorCreatingConnection" +
-                                " msg.what=EVENT_DEACTIVATE_DONE";
-                        if (DBG) log(str);
-                        ApnContext.requestLog(cp.mApnContext, str);
-
-                        // Transition to inactive but send notifications after
-                        // we've entered the mInactive state.
-                        mInactiveState.setEnterNotificationParams(cp,
-                                DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
-                                DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
-                        transitionTo(mInactiveState);
-                    } else {
-                        if (DBG) {
-                            log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
-                                    + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
-                        }
-                    }
-                    retVal = HANDLED;
-                    break;
-
-                default:
-                    if (VDBG) {
-                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
-                                + getWhatToString(msg.what));
-                    }
-                    retVal = NOT_HANDLED;
-                    break;
-            }
-            return retVal;
-        }
-    }
-    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
-                new DcDisconnectionErrorCreatingConnection();
-
-    /**
-     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
-     * Used for cellular networks that use Access Point Names (APN) such
-     * as GSM networks.
-     *
-     * @param apnContext is the Access Point Name to bring up a connection to
-     * @param profileId for the connection
-     * @param rilRadioTechnology Radio technology for the data connection
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *                       With AsyncResult.userObj set to the original msg.obj,
-     *                       AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     * @param connectionGeneration used to track a single connection request so disconnects can get
-     *                             ignored if obsolete.
-     * @param requestType Data request type
-     * @param subId the subscription id associated with this data connection.
-     * @param isApnPreferred whether or not the apn is preferred.
-     */
-    public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
-                        Message onCompletedMsg, int connectionGeneration,
-                        @RequestNetworkType int requestType, int subId, boolean isApnPreferred) {
-        if (DBG) {
-            log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
-        }
-
-        if (mApnSetting == null) {
-            mApnSetting = apnContext.getApnSetting();
-        }
-
-        sendMessage(DataConnection.EVENT_CONNECT,
-                new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
-                        connectionGeneration, requestType, subId, isApnPreferred));
-    }
-
-    /**
-     * Tear down the connection through the apn on the network.
-     *
-     * @param apnContext APN context
-     * @param reason reason to tear down
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj.
-     */
-    public void tearDown(ApnContext apnContext, String reason, Message onCompletedMsg) {
-        if (DBG) {
-            log("tearDown: apnContext=" + apnContext + " reason=" + reason + " onCompletedMsg="
-                    + onCompletedMsg);
-        }
-        sendMessage(DataConnection.EVENT_DISCONNECT,
-                new DisconnectParams(apnContext, reason, DcTracker.RELEASE_TYPE_DETACH,
-                        onCompletedMsg));
-    }
-
-    // ******* "public" interface
-
-    /**
-     * Used for testing purposes.
-     */
-    void tearDownNow() {
-        if (DBG) log("tearDownNow()");
-        sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
-    }
-
-    /**
-     * Tear down the connection through the apn on the network.  Ignores reference count and
-     * and always tears down.
-     *
-     * @param releaseType Data release type
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj.
-     */
-    public void tearDownAll(String reason, @ReleaseNetworkType int releaseType,
-                            Message onCompletedMsg) {
-        if (DBG) {
-            log("tearDownAll: reason=" + reason + ", releaseType="
-                    + DcTracker.releaseTypeToString(releaseType));
-        }
-        sendMessage(DataConnection.EVENT_DISCONNECT_ALL,
-                new DisconnectParams(null, reason, releaseType, onCompletedMsg));
-    }
-
-    /**
-     * Reset the data connection to inactive state.
-     */
-    public void reset() {
-        sendMessage(EVENT_RESET);
-        if (DBG) log("reset");
-    }
-
-    /**
-     * Re-evaluate the restricted state. If the restricted data connection does not need to be
-     * restricted anymore, we need to dynamically change the network's capability.
-     */
-    void reevaluateRestrictedState() {
-        sendMessage(EVENT_REEVALUATE_RESTRICTED_STATE);
-        if (DBG) log("reevaluate restricted state");
-    }
-
-    /**
-     * Re-evaluate the data connection properties. For example, it will recalculate data connection
-     * score and update through network agent it if changed.
-     */
-    void reevaluateDataConnectionProperties() {
-        sendMessage(EVENT_REEVALUATE_DATA_CONNECTION_PROPERTIES);
-        if (DBG) log("reevaluate data connection properties");
-    }
-
-    /**
-     * @return The parameters used for initiating a data connection.
-     */
-    public ConnectionParams getConnectionParams() {
-        return mConnectionParams;
-    }
-
-    /**
-     * Update PCSCF addresses
-     *
-     * @param response
-     */
-    public void updatePcscfAddr(DataCallResponse response) {
-        mPcscfAddr = response.getPcscfAddresses().stream()
-                .map(InetAddress::getHostAddress).toArray(String[]::new);
-    }
-
-    /**
-     * @return The list of PCSCF addresses
-     */
-    public String[] getPcscfAddresses() {
-        return mPcscfAddr;
-    }
-
-    /**
-     * Using the result of the SETUP_DATA_CALL determine the retry delay.
-     *
-     * @param response The response from setup data call
-     * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested.
-     * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds
-     * to the next SETUP_DATA_CALL.
-     */
-    private long getSuggestedRetryDelay(DataCallResponse response) {
-        /** According to ril.h
-         * The value < 0 means no value is suggested
-         * The value 0 means retry should be done ASAP.
-         * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry.
-         */
-        if (response == null) {
-            return RetryManager.NO_SUGGESTED_RETRY_DELAY;
-        }
-
-        long suggestedRetryTime = response.getRetryDurationMillis();
-
-        // The value < 0 means no value is suggested
-        if (suggestedRetryTime < 0) {
-            if (DBG) log("No suggested retry delay.");
-            return RetryManager.NO_SUGGESTED_RETRY_DELAY;
-        } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)
-                && suggestedRetryTime == Long.MAX_VALUE) {
-            if (DBG) log("Network suggested not retrying.");
-            return RetryManager.NO_RETRY;
-        } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6)
-                && suggestedRetryTime == Integer.MAX_VALUE) {
-            if (DBG) log("Network suggested not retrying.");
-            return RetryManager.NO_RETRY;
-        }
-
-        return suggestedRetryTime;
-    }
-
-    public List<ApnContext> getApnContexts() {
-        return new ArrayList<>(mApnContexts.keySet());
-    }
-
-    /**
-     * Return whether there is an ApnContext for the given type in this DataConnection.
-     * @param type APN type to check
-     * @param exclusive true if the given APN type should be the only APN type that exists
-     * @return True if there is an ApnContext for the given type
-     */
-    private boolean isApnContextAttached(@ApnType int type, boolean exclusive) {
-        boolean attached = mApnContexts.keySet().stream()
-                .map(ApnContext::getApnTypeBitmask)
-                .anyMatch(bitmask -> bitmask == type);
-        if (exclusive) {
-            attached &= mApnContexts.size() == 1;
-        }
-        return attached;
-    }
-
-    /** Get the network agent of the data connection */
-    @Nullable
-    DcNetworkAgent getNetworkAgent() {
-        return mNetworkAgent;
-    }
-
-    void setHandoverState(@HandoverState int state) {
-        if (mHandoverState != state) {
-            String logStr = "State changed from " + handoverStateToString(mHandoverState)
-                    + " to " + handoverStateToString(state);
-            mHandoverLocalLog.log(logStr);
-            logd(logStr);
-            mHandoverState = state;
-        }
-    }
-
-    /** Sets the {@link DataCallSessionStats} mock for this data connection during unit testing. */
-    @VisibleForTesting
-    public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) {
-        mDataCallSessionStats = dataCallSessionStats;
-    }
-
-    /**
-     * @return the string for msg.what as our info.
-     */
-    @Override
-    protected String getWhatToString(int what) {
-        return cmdToString(what);
-    }
-
-    private static String msgToString(Message msg) {
-        String retVal;
-        if (msg == null) {
-            retVal = "null";
-        } else {
-            StringBuilder   b = new StringBuilder();
-
-            b.append("{what=");
-            b.append(cmdToString(msg.what));
-
-            b.append(" when=");
-            TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
-
-            if (msg.arg1 != 0) {
-                b.append(" arg1=");
-                b.append(msg.arg1);
-            }
-
-            if (msg.arg2 != 0) {
-                b.append(" arg2=");
-                b.append(msg.arg2);
-            }
-
-            if (msg.obj != null) {
-                b.append(" obj=");
-                b.append(msg.obj);
-            }
-
-            b.append(" target=");
-            b.append(msg.getTarget());
-
-            b.append(" replyTo=");
-            b.append(msg.replyTo);
-
-            b.append("}");
-
-            retVal = b.toString();
-        }
-        return retVal;
-    }
-
-    static void slog(String s) {
-        Rlog.d("DC", s);
-    }
-
-    /**
-     * Log with debug
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void log(String s) {
-        Rlog.d(getName(), s);
-    }
-
-    /**
-     * Log with debug attribute
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void logd(String s) {
-        Rlog.d(getName(), s);
-    }
-
-    /**
-     * Log with verbose attribute
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void logv(String s) {
-        Rlog.v(getName(), s);
-    }
-
-    /**
-     * Log with info attribute
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void logi(String s) {
-        Rlog.i(getName(), s);
-    }
-
-    /**
-     * Log with warning attribute
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void logw(String s) {
-        Rlog.w(getName(), s);
-    }
-
-    /**
-     * Log with error attribute
-     *
-     * @param s is string log
-     */
-    @Override
-    protected void loge(String s) {
-        Rlog.e(getName(), s);
-    }
-
-    /**
-     * Log with error attribute
-     *
-     * @param s is string log
-     * @param e is a Throwable which logs additional information.
-     */
-    @Override
-    protected void loge(String s, Throwable e) {
-        Rlog.e(getName(), s, e);
-    }
-
-    /** Doesn't print mApnList of ApnContext's which would be recursive */
-    public synchronized String toStringSimple() {
-        return getName() + ": State=" + getCurrentState().getName()
-                + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
-                + " mCid=" + mCid + " mCreateTime=" + mCreateTime
-                + " mLastastFailTime=" + mLastFailTime
-                + " mLastFailCause=" + DataFailCause.toString(mLastFailCause)
-                + " mTag=" + mTag
-                + " mLinkProperties=" + mLinkProperties
-                + " linkCapabilities=" + getNetworkCapabilities()
-                + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride;
-    }
-
-    @Override
-    public String toString() {
-        return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
-    }
-
-    /** Check if the device is connected to NR 5G Non-Standalone network. */
-    private boolean isNRConnected() {
-        return mPhone.getServiceState().getNrState()
-                == NetworkRegistrationInfo.NR_STATE_CONNECTED;
-    }
-
-    /**
-     * @return The disallowed APN types bitmask
-     */
-    private @ApnType int getDisallowedApnTypes() {
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        int apnTypesBitmask = 0;
-        if (configManager != null) {
-            PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
-            if (bundle != null) {
-                String key = (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        ? CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY
-                        : CarrierConfigManager.KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY;
-                if (bundle.getStringArray(key) != null) {
-                    String disallowedApnTypesString =
-                            TextUtils.join(",", bundle.getStringArray(key));
-                    if (!TextUtils.isEmpty(disallowedApnTypesString)) {
-                        apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString(
-                                disallowedApnTypesString);
-                    }
-                }
-            }
-        }
-
-        return apnTypesBitmask;
-    }
-
-    private void dumpToLog() {
-        dump(null, new PrintWriter(new StringWriter(0)) {
-            @Override
-            public void println(String s) {
-                DataConnection.this.logd(s);
-            }
-
-            @Override
-            public void flush() {
-            }
-        }, null);
-    }
-
-    /**
-     *  Re-calculate score and update through network agent if it changes.
-     */
-    private void updateScore() {
-        int oldScore = mScore;
-        mScore = calculateScore();
-        if (oldScore != mScore && mNetworkAgent != null) {
-            log("Updating score from " + oldScore + " to " + mScore);
-            mNetworkAgent.sendNetworkScore(mScore, this);
-        }
-    }
-
-    private int calculateScore() {
-        int score = OTHER_CONNECTION_SCORE;
-
-        // If it's serving a network request that asks NET_CAPABILITY_INTERNET and doesn't have
-        // specify a subId, this dataConnection is considered to be default Internet data
-        // connection. In this case we assign a slightly higher score of 50. The intention is
-        // it will not be replaced by other data connections accidentally in DSDS usecase.
-        for (ApnContext apnContext : mApnContexts.keySet()) {
-            for (NetworkRequest networkRequest : apnContext.getNetworkRequests()) {
-                if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                        && networkRequest.getNetworkSpecifier() == null) {
-                    score = DEFAULT_INTERNET_CONNECTION_SCORE;
-                    break;
-                }
-            }
-        }
-
-        return score;
-    }
-
-    private String handoverStateToString(@HandoverState int state) {
-        switch (state) {
-            case HANDOVER_STATE_IDLE: return "IDLE";
-            case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED";
-            case HANDOVER_STATE_COMPLETED: return "COMPLETED";
-            default: return "UNKNOWN";
-        }
-    }
-
-    private @DataState int getState() {
-        if (isInactive()) {
-            return TelephonyManager.DATA_DISCONNECTED;
-        } else if (isActivating()) {
-            return TelephonyManager.DATA_CONNECTING;
-        } else if (isActive()) {
-            // The data connection can only be suspended when it's in active state.
-            if (mIsSuspended) {
-                return TelephonyManager.DATA_SUSPENDED;
-            }
-            return TelephonyManager.DATA_CONNECTED;
-        } else if (isDisconnecting()) {
-            return TelephonyManager.DATA_DISCONNECTING;
-        }
-
-        return TelephonyManager.DATA_UNKNOWN;
-    }
-
-    /**
-     * Get precise data connection state
-     *
-     * @return The {@link PreciseDataConnectionState}
-     */
-    public PreciseDataConnectionState getPreciseDataConnectionState() {
-        return new PreciseDataConnectionState.Builder()
-                .setTransportType(mTransportType)
-                .setId(mCid)
-                .setState(getState())
-                .setApnSetting(mApnSetting)
-                .setLinkProperties(mLinkProperties)
-                .setNetworkType(getNetworkType())
-                .setFailCause(mDcFailCause)
-                .build();
-    }
-
-    /**
-     * Dump the current state.
-     *
-     * @param fd
-     * @param printWriter
-     * @param args
-     */
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
-        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
-        pw.print("DataConnection ");
-        super.dump(fd, pw, args);
-        pw.flush();
-        pw.increaseIndent();
-        pw.println("transport type="
-                + AccessNetworkConstants.transportTypeToString(mTransportType));
-        pw.println("mApnContexts.size=" + mApnContexts.size());
-        pw.println("mApnContexts=" + mApnContexts);
-        pw.println("mApnSetting=" + mApnSetting);
-        pw.println("mTag=" + mTag);
-        pw.println("mCid=" + mCid);
-        pw.println("mConnectionParams=" + mConnectionParams);
-        pw.println("mDisconnectParams=" + mDisconnectParams);
-        pw.println("mDcFailCause=" + DataFailCause.toString(mDcFailCause));
-        pw.println("mPhone=" + mPhone);
-        pw.println("mSubId=" + mSubId);
-        pw.println("mLinkProperties=" + mLinkProperties);
-        pw.flush();
-        pw.println("mDataRegState=" + mDataRegState);
-        pw.println("mHandoverState=" + handoverStateToString(mHandoverState));
-        pw.println("mRilRat=" + mRilRat);
-        pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
-        pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
-        pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
-        pw.println("mLastFailCause=" + DataFailCause.toString(mLastFailCause));
-        pw.println("mUserData=" + mUserData);
-        pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride);
-        pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
-        pw.println("mMmsUseOnly=" + mMmsUseOnly);
-        pw.println("mEnterpriseUse=" + mEnterpriseUse);
-        pw.println("mUnmeteredOverride=" + mUnmeteredOverride);
-        pw.println("mCongestedOverride=" + mCongestedOverride);
-        pw.println("mDownlinkBandwidth" + mDownlinkBandwidth);
-        pw.println("mUplinkBandwidth=" + mUplinkBandwidth);
-        pw.println("mDefaultQos=" + mDefaultQos);
-        pw.println("mQosBearerSessions=" + mQosBearerSessions);
-        pw.println("disallowedApnTypes="
-                + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes()));
-        pw.println("mInstanceNumber=" + mInstanceNumber);
-        pw.println("mAc=" + mAc);
-        pw.println("mScore=" + mScore);
-        if (mNetworkAgent != null) {
-            mNetworkAgent.dump(fd, pw, args);
-        }
-        pw.println("handover local log:");
-        pw.increaseIndent();
-        mHandoverLocalLog.dump(fd, pw, args);
-        pw.decreaseIndent();
-        pw.decreaseIndent();
-        pw.println();
-        pw.flush();
-    }
-
-    /**
-     * Class used to track VCN-defined Network policies for this DataConnection.
-     *
-     * <p>MUST be registered with the associated DataConnection's Handler.
-     */
-    private class DataConnectionVcnNetworkPolicyChangeListener
-            implements VcnNetworkPolicyChangeListener {
-        @Override
-        public void onPolicyChanged() {
-            // Poll the current underlying Network policy from VcnManager and send to NetworkAgent.
-            final NetworkCapabilities networkCapabilities = getNetworkCapabilities();
-            VcnNetworkPolicyResult policyResult =
-                    mVcnManager.applyVcnNetworkPolicy(
-                            networkCapabilities, getLinkProperties());
-            if (policyResult.isTeardownRequested()) {
-                tearDownAll(
-                        Phone.REASON_VCN_REQUESTED_TEARDOWN,
-                        DcTracker.RELEASE_TYPE_DETACH,
-                        null /* onCompletedMsg */);
-            }
-
-            if (mNetworkAgent != null) {
-                mNetworkAgent.sendNetworkCapabilities(networkCapabilities, DataConnection.this);
-            }
-        }
-    }
-}
-
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
deleted file mode 100644
index 68f2ab3..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.HashSet;
-
-/**
- * The class to describe the reasons of allowing or disallowing to establish a data connection.
- */
-public class DataConnectionReasons {
-    private HashSet<DataDisallowedReasonType> mDataDisallowedReasonSet = new HashSet<>();
-    private DataAllowedReasonType mDataAllowedReason = DataAllowedReasonType.NONE;
-
-    public DataConnectionReasons() {}
-
-    void add(DataDisallowedReasonType reason) {
-        // Adding a disallowed reason will clean up the allowed reason because they are
-        // mutual exclusive.
-        mDataAllowedReason = DataAllowedReasonType.NONE;
-        mDataDisallowedReasonSet.add(reason);
-    }
-
-    void add(DataAllowedReasonType reason) {
-        // Adding an allowed reason will clean up the disallowed reasons because they are
-        // mutual exclusive.
-        mDataDisallowedReasonSet.clear();
-
-        // Only higher priority allowed reason can overwrite the old one. See
-        // DataAllowedReasonType for the oder.
-        if (reason.ordinal() > mDataAllowedReason.ordinal()) {
-            mDataAllowedReason = reason;
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder reasonStr = new StringBuilder();
-        if (mDataDisallowedReasonSet.size() > 0) {
-            reasonStr.append("Data disallowed reasons:");
-            for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
-                reasonStr.append(" ").append(reason);
-            }
-        } else {
-            reasonStr.append("Data allowed reason:");
-            reasonStr.append(" ").append(mDataAllowedReason);
-        }
-        return reasonStr.toString();
-    }
-
-    void copyFrom(DataConnectionReasons reasons) {
-        this.mDataDisallowedReasonSet = reasons.mDataDisallowedReasonSet;
-        this.mDataAllowedReason = reasons.mDataAllowedReason;
-    }
-
-    boolean allowed() {
-        return mDataDisallowedReasonSet.size() == 0;
-    }
-
-    /**
-     * Check if it contains a certain disallowed reason.
-     *
-     * @param reason The disallowed reason to check.
-     * @return {@code true} if the provided reason matches one of the disallowed reasons.
-     */
-    @VisibleForTesting
-    public boolean contains(DataDisallowedReasonType reason) {
-        return mDataDisallowedReasonSet.contains(reason);
-    }
-
-    /**
-     * Check if only one disallowed reason prevent data connection.
-     *
-     * @param reason The given reason to check
-     * @return True if the given reason is the only one that prevents data connection
-     */
-    public boolean containsOnly(DataDisallowedReasonType reason) {
-        return mDataDisallowedReasonSet.size() == 1 && contains(reason);
-    }
-
-    boolean contains(DataAllowedReasonType reason) {
-        return reason == mDataAllowedReason;
-    }
-
-    boolean containsHardDisallowedReasons() {
-        for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
-            if (reason.isHardReason()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Disallowed reasons. There could be multiple reasons if data connection is not allowed.
-    public enum DataDisallowedReasonType {
-        // Soft failure reasons. Normally the reasons from users or policy settings.
-
-        // Data is disabled by the user or policy.
-        DATA_DISABLED(false),
-        // Data roaming is disabled by the user.
-        ROAMING_DISABLED(false),
-        // Default data not selected.
-        DEFAULT_DATA_UNSELECTED(false),
-
-        // Belows are all hard failure reasons.
-        NOT_ATTACHED(true),
-        SIM_NOT_READY(true),
-        INVALID_PHONE_STATE(true),
-        CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
-        PS_RESTRICTED(true),
-        UNDESIRED_POWER_STATE(true),
-        INTERNAL_DATA_DISABLED(true),
-        RADIO_DISABLED_BY_CARRIER(true),
-        // Not in the right state for data call setup.
-        APN_NOT_CONNECTABLE(true),
-        // Data is in connecting state. No need to send another setup request.
-        DATA_IS_CONNECTING(true),
-        // Data is being disconnected. Telephony will retry after disconnected.
-        DATA_IS_DISCONNECTING(true),
-        // Data is already connected. No need to setup data again.
-        DATA_ALREADY_CONNECTED(true),
-        // certain APNs are not allowed on IWLAN in legacy mode.
-        ON_IWLAN(true),
-        // certain APNs are only allowed when the device is camped on NR.
-        NOT_ON_NR(true),
-        // Data is not allowed while device is in emergency callback mode.
-        IN_ECBM(true),
-        // The given APN type's preferred transport has switched.
-        ON_OTHER_TRANSPORT(true),
-        // Underlying data service is not bound.
-        DATA_SERVICE_NOT_READY(true),
-        // Qualified networks service does not allow certain types of APN brought up on either
-        // cellular or IWLAN.
-        DISABLED_BY_QNS(true),
-        // Data is throttled. The network explicitly requested device not to establish data
-        // connection for a certain period.
-        DATA_THROTTLED(true);
-
-        private boolean mIsHardReason;
-
-        boolean isHardReason() {
-            return mIsHardReason;
-        }
-
-        DataDisallowedReasonType(boolean isHardReason) {
-            mIsHardReason = isHardReason;
-        }
-    }
-
-    // Data allowed reasons. There will be only one reason if data is allowed.
-    enum DataAllowedReasonType {
-        // Note that unlike disallowed reasons, we only have one allowed reason every time
-        // when we check data is allowed or not. The order of these allowed reasons is very
-        // important. The lower ones take precedence over the upper ones.
-        NONE,
-        NORMAL,
-        UNMETERED_APN,
-        RESTRICTED_REQUEST,
-        EMERGENCY_APN,
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
deleted file mode 100644
index 305b4a8..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-
-import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE;
-import static android.telephony.PhoneStateListener.LISTEN_NONE;
-
-import android.annotation.IntDef;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.Handler;
-import android.os.RegistrantList;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.sysprop.TelephonyProperties;
-import android.telephony.Annotation.CallState;
-import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.util.LocalLog;
-import android.util.Pair;
-
-import com.android.internal.telephony.GlobalSettingsHelper;
-import com.android.internal.telephony.MultiSimSettingController;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.data.DataEnabledOverride;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * The class to hold different data enabled/disabled settings. Also it allows clients to register
- * for overall data enabled setting changed event.
- * @hide
- */
-public class DataEnabledSettings {
-
-    private static final String LOG_TAG = "DataEnabledSettings";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"REASON_"},
-            value = {
-                    REASON_REGISTERED,
-                    REASON_INTERNAL_DATA_ENABLED,
-                    REASON_USER_DATA_ENABLED,
-                    REASON_POLICY_DATA_ENABLED,
-                    REASON_DATA_ENABLED_BY_CARRIER,
-                    REASON_PROVISIONED_CHANGED,
-                    REASON_PROVISIONING_DATA_ENABLED_CHANGED,
-                    REASON_OVERRIDE_RULE_CHANGED,
-                    REASON_OVERRIDE_CONDITION_CHANGED,
-                    REASON_THERMAL_DATA_ENABLED
-            })
-    public @interface DataEnabledChangedReason {}
-
-    public static final int REASON_REGISTERED = 0;
-
-    public static final int REASON_INTERNAL_DATA_ENABLED = 1;
-
-    public static final int REASON_USER_DATA_ENABLED = 2;
-
-    public static final int REASON_POLICY_DATA_ENABLED = 3;
-
-    public static final int REASON_DATA_ENABLED_BY_CARRIER = 4;
-
-    public static final int REASON_PROVISIONED_CHANGED = 5;
-
-    public static final int REASON_PROVISIONING_DATA_ENABLED_CHANGED = 6;
-
-    public static final int REASON_OVERRIDE_RULE_CHANGED = 7;
-
-    public static final int REASON_OVERRIDE_CONDITION_CHANGED = 8;
-
-    public static final int REASON_THERMAL_DATA_ENABLED = 9;
-
-    /**
-     * responds to the setInternalDataEnabled call - used internally to turn off data.
-     * For example during emergency calls
-     */
-    private boolean mInternalDataEnabled = true;
-
-    /**
-     * Flag indicating data allowed by network policy manager or not.
-     */
-    private boolean mPolicyDataEnabled = true;
-
-    /**
-     * Indicate if metered APNs are enabled by the carrier. set false to block all the metered APNs
-     * from continuously sending requests, which causes undesired network load.
-     */
-    private boolean mCarrierDataEnabled = true;
-
-    /**
-     * Flag indicating data allowed by Thermal service or not.
-     */
-    private boolean mThermalDataEnabled = true;
-
-    /**
-     * Flag indicating whether data is allowed or not for the device. It can be disabled by
-     * user, carrier, policy or thermal
-     */
-    private boolean mIsDataEnabled = false;
-
-    private final Phone mPhone;
-
-    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-    private ContentResolver mResolver = null;
-
-    private final RegistrantList mOverallDataEnabledChangedRegistrants = new RegistrantList();
-
-    // TODO: Merge this with mOverallDataEnabledChangedRegistrants. In the future, notifying data
-    // enabled changed with APN types bitmask
-    private final RegistrantList mOverallDataEnabledOverrideChangedRegistrants =
-            new RegistrantList();
-
-    private final LocalLog mSettingChangeLocalLog = new LocalLog(32);
-
-    private DataEnabledOverride mDataEnabledOverride;
-
-    private TelephonyManager mTelephonyManager;
-
-    // for msim, user data enabled setting depends on subId.
-    private final SubscriptionManager.OnSubscriptionsChangedListener
-            mOnSubscriptionsChangeListener =
-            new SubscriptionManager.OnSubscriptionsChangedListener() {
-                @Override
-                public void onSubscriptionsChanged() {
-                    synchronized (this) {
-                        if (mSubId != mPhone.getSubId()) {
-                            log("onSubscriptionsChanged subId: " + mSubId + " to: "
-                                    + mPhone.getSubId());
-                            mSubId = mPhone.getSubId();
-                            mDataEnabledOverride = getDataEnabledOverride();
-                            updatePhoneStateListener();
-                            updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
-                            mPhone.notifyUserMobileDataStateChanged(isUserDataEnabled());
-                        }
-                    }
-                }
-            };
-
-    private void updatePhoneStateListener() {
-        mTelephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
-        if (SubscriptionManager.isUsableSubscriptionId(mSubId)) {
-            mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
-        }
-        mTelephonyManager.listen(mPhoneStateListener, LISTEN_CALL_STATE);
-    }
-
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        @Override
-        public void onCallStateChanged(@CallState int state, String phoneNumber) {
-            updateDataEnabledAndNotify(REASON_OVERRIDE_CONDITION_CHANGED);
-        }
-    };
-
-    @Override
-    public String toString() {
-        return "[mInternalDataEnabled=" + mInternalDataEnabled
-                + ", isUserDataEnabled=" + isUserDataEnabled()
-                + ", isProvisioningDataEnabled=" + isProvisioningDataEnabled()
-                + ", mPolicyDataEnabled=" + mPolicyDataEnabled
-                + ", mCarrierDataEnabled=" + mCarrierDataEnabled
-                + ", mIsDataEnabled=" + mIsDataEnabled
-                + ", mThermalDataEnabled=" + mThermalDataEnabled
-                + ", " + mDataEnabledOverride
-                + "]";
-    }
-
-    public DataEnabledSettings(Phone phone) {
-        mPhone = phone;
-        mResolver = mPhone.getContext().getContentResolver();
-        SubscriptionManager subscriptionManager = (SubscriptionManager) mPhone.getContext()
-                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-        subscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
-        mTelephonyManager = (TelephonyManager) mPhone.getContext()
-                .getSystemService(Context.TELEPHONY_SERVICE);
-        mDataEnabledOverride = getDataEnabledOverride();
-        updateDataEnabled();
-    }
-
-    private DataEnabledOverride getDataEnabledOverride() {
-        return new DataEnabledOverride(SubscriptionController.getInstance()
-                .getDataEnabledOverrideRules(mPhone.getSubId()));
-    }
-
-    public synchronized void setInternalDataEnabled(boolean enabled) {
-        if (mInternalDataEnabled != enabled) {
-            localLog("InternalDataEnabled", enabled);
-            mInternalDataEnabled = enabled;
-            updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED);
-        }
-    }
-    public synchronized boolean isInternalDataEnabled() {
-        return mInternalDataEnabled;
-    }
-
-    private synchronized void setUserDataEnabled(boolean enabled) {
-        // By default the change should propagate to the group.
-        setUserDataEnabled(enabled, true);
-    }
-
-    /**
-     * @param notifyMultiSimSettingController if setUserDataEnabled is already from propagating
-     *        from MultiSimSettingController, don't notify MultiSimSettingController again.
-     *        For example, if sub1 and sub2 are in the same group and user enables data for sub
-     *        1, sub 2 will also be enabled but with propagateToGroup = false.
-     */
-    public synchronized void setUserDataEnabled(boolean enabled,
-            boolean notifyMultiSimSettingController) {
-        // Can't disable data for stand alone opportunistic subscription.
-        if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext()) && !enabled) return;
-
-        boolean changed = GlobalSettingsHelper.setInt(mPhone.getContext(),
-                Settings.Global.MOBILE_DATA, mPhone.getSubId(), (enabled ? 1 : 0));
-        if (changed) {
-            localLog("UserDataEnabled", enabled);
-            mPhone.notifyUserMobileDataStateChanged(enabled);
-            updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
-            if (notifyMultiSimSettingController) {
-                MultiSimSettingController.getInstance().notifyUserDataEnabled(
-                        mPhone.getSubId(), enabled);
-            }
-        }
-    }
-
-    /**
-     * Policy control of data connection with reason
-     * @param reason the reason the data enable change is taking place
-     * @param enabled True if enabling the data, otherwise disabling.
-     */
-    public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason,
-            boolean enabled) {
-        switch (reason) {
-            case TelephonyManager.DATA_ENABLED_REASON_USER:
-                setUserDataEnabled(enabled);
-                break;
-            case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
-                setCarrierDataEnabled(enabled);
-                break;
-            case TelephonyManager.DATA_ENABLED_REASON_POLICY:
-                setPolicyDataEnabled(enabled);
-                break;
-            case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
-                setThermalDataEnabled(enabled);
-                break;
-            default:
-                log("Invalid data enable reason " + reason);
-                break;
-        }
-    }
-
-    public synchronized boolean isUserDataEnabled() {
-        // User data should always be true for opportunistic subscription.
-        if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext())) return true;
-
-        boolean defaultVal = TelephonyProperties.mobile_data().orElse(true);
-
-        return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
-                Settings.Global.MOBILE_DATA, mPhone.getSubId(), defaultVal);
-    }
-
-    /**
-     * Set whether always allowing MMS data connection.
-     *
-     * @param alwaysAllow {@code true} if MMS data is always allowed.
-     *
-     * @return {@code false} if the setting is changed.
-     */
-    public synchronized boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
-        localLog("setAlwaysAllowMmsData", alwaysAllow);
-        mDataEnabledOverride.setAlwaysAllowMms(alwaysAllow);
-        boolean changed = SubscriptionController.getInstance()
-                .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
-        if (changed) {
-            updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED);
-            notifyDataEnabledOverrideChanged();
-        }
-
-        return changed;
-    }
-
-    /**
-     * Set allowing mobile data during voice call. This is used for allowing data on the non-default
-     * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
-     * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
-     * non-default data SIM during the life cycle of the voice call.
-     *
-     * @param allow {@code true} if allowing using data during voice call, {@code false} if
-     * disallowed
-     *
-     * @return {@code true} if operation is successful. otherwise {@code false}.
-     */
-    public synchronized boolean setAllowDataDuringVoiceCall(boolean allow) {
-        localLog("setAllowDataDuringVoiceCall", allow);
-        if (allow == isDataAllowedInVoiceCall()) {
-            return true;
-        }
-        mDataEnabledOverride.setDataAllowedInVoiceCall(allow);
-
-        boolean changed = SubscriptionController.getInstance()
-                .setDataEnabledOverrideRules(mPhone.getSubId(), mDataEnabledOverride.getRules());
-        if (changed) {
-            updateDataEnabledAndNotify(REASON_OVERRIDE_RULE_CHANGED);
-            notifyDataEnabledOverrideChanged();
-        }
-
-        return changed;
-    }
-
-    /**
-     * Check if data is allowed during voice call.
-     *
-     * @return {@code true} if data is allowed during voice call.
-     */
-    public synchronized boolean isDataAllowedInVoiceCall() {
-        return mDataEnabledOverride.isDataAllowedInVoiceCall();
-    }
-
-    public synchronized boolean isMmsAlwaysAllowed() {
-        return mDataEnabledOverride.isMmsAlwaysAllowed();
-    }
-
-    private synchronized void setPolicyDataEnabled(boolean enabled) {
-        if (mPolicyDataEnabled != enabled) {
-            localLog("PolicyDataEnabled", enabled);
-            mPolicyDataEnabled = enabled;
-            updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED);
-        }
-    }
-
-    public synchronized boolean isPolicyDataEnabled() {
-        return mPolicyDataEnabled;
-    }
-
-    private synchronized void setCarrierDataEnabled(boolean enabled) {
-        if (mCarrierDataEnabled != enabled) {
-            localLog("CarrierDataEnabled", enabled);
-            mCarrierDataEnabled = enabled;
-            updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER);
-        }
-    }
-
-    public synchronized boolean isCarrierDataEnabled() {
-        return mCarrierDataEnabled;
-    }
-
-    private synchronized void setThermalDataEnabled(boolean enabled) {
-        if (mThermalDataEnabled != enabled) {
-            localLog("ThermalDataEnabled", enabled);
-            mThermalDataEnabled = enabled;
-            updateDataEnabledAndNotify(REASON_THERMAL_DATA_ENABLED);
-        }
-    }
-
-    public synchronized boolean isThermalDataEnabled() {
-        return mThermalDataEnabled;
-    }
-
-    public synchronized void updateProvisionedChanged() {
-        updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED);
-    }
-
-    public synchronized void updateProvisioningDataEnabled() {
-        updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED);
-    }
-
-    public synchronized boolean isDataEnabled() {
-        return mIsDataEnabled;
-    }
-
-    /**
-     * Check if data is enabled for a specific reason {@@TelephonyManager.DataEnabledReason}
-     *
-     * @return {@code true} if the overall data is enabled; {@code false} if not.
-     */
-    public synchronized boolean isDataEnabledForReason(
-            @TelephonyManager.DataEnabledReason int reason) {
-        switch (reason) {
-            case TelephonyManager.DATA_ENABLED_REASON_USER:
-                return isUserDataEnabled();
-            case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
-                return isCarrierDataEnabled();
-            case TelephonyManager.DATA_ENABLED_REASON_POLICY:
-                return isPolicyDataEnabled();
-            case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
-                return isThermalDataEnabled();
-            default:
-                return false;
-        }
-    }
-
-    private synchronized void updateDataEnabledAndNotify(int reason) {
-        boolean prevDataEnabled = mIsDataEnabled;
-
-        updateDataEnabled();
-
-        if (prevDataEnabled != mIsDataEnabled) {
-            notifyDataEnabledChanged(!prevDataEnabled, reason);
-        }
-    }
-
-    private synchronized void updateDataEnabled() {
-        if (isProvisioning()) {
-            mIsDataEnabled = isProvisioningDataEnabled();
-        } else {
-            mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride
-                    .shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL))
-                    && mPolicyDataEnabled && mCarrierDataEnabled && mThermalDataEnabled;
-        }
-    }
-
-    public boolean isProvisioning() {
-        return Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0;
-    }
-    /**
-     * In provisioning, we might want to have enable mobile data during provisioning. It depends
-     * on value of Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED which is set by
-     * setupwizard. It only matters if it's in provisioning stage.
-     * @return whether we are enabling userData during provisioning stage.
-     */
-    public boolean isProvisioningDataEnabled() {
-        final String prov_property = SystemProperties.get("ro.com.android.prov_mobiledata",
-                "false");
-        boolean retVal = "true".equalsIgnoreCase(prov_property);
-
-        final int prov_mobile_data = Settings.Global.getInt(mResolver,
-                Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
-                retVal ? 1 : 0);
-        retVal = prov_mobile_data != 0;
-        log("getDataEnabled during provisioning retVal=" + retVal + " - (" + prov_property
-                + ", " + prov_mobile_data + ")");
-
-        return retVal;
-    }
-
-    public synchronized void setDataRoamingEnabled(boolean enabled) {
-        // will trigger handleDataOnRoamingChange() through observer
-        boolean changed = GlobalSettingsHelper.setBoolean(mPhone.getContext(),
-                Settings.Global.DATA_ROAMING, mPhone.getSubId(), enabled);
-
-        if (changed) {
-            localLog("setDataRoamingEnabled", enabled);
-            MultiSimSettingController.getInstance().notifyRoamingDataEnabled(mPhone.getSubId(),
-                    enabled);
-        }
-    }
-
-    /**
-     * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
-     */
-    public synchronized boolean getDataRoamingEnabled() {
-        return GlobalSettingsHelper.getBoolean(mPhone.getContext(),
-                Settings.Global.DATA_ROAMING, mPhone.getSubId(), getDefaultDataRoamingEnabled());
-    }
-
-    /**
-     * get default values for {@link Settings.Global#DATA_ROAMING}
-     * return {@code true} if either
-     * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or
-     * system property ro.com.android.dataroaming is set to true. otherwise return {@code false}
-     */
-    public synchronized boolean getDefaultDataRoamingEnabled() {
-        final CarrierConfigManager configMgr = (CarrierConfigManager)
-                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
-                "ro.com.android.dataroaming", "false"));
-        isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean(
-                CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL);
-        return isDataRoamingEnabled;
-    }
-
-    private void notifyDataEnabledChanged(boolean enabled, int reason) {
-        mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
-        mPhone.notifyDataEnabled(enabled, reason);
-    }
-
-    public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
-        mOverallDataEnabledChangedRegistrants.addUnique(h, what, obj);
-        notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED);
-    }
-
-    public void unregisterForDataEnabledChanged(Handler h) {
-        mOverallDataEnabledChangedRegistrants.remove(h);
-    }
-
-    private void notifyDataEnabledOverrideChanged() {
-        mOverallDataEnabledOverrideChangedRegistrants.notifyRegistrants();
-    }
-
-    /**
-     * Register for data enabled override changed event.
-     *
-     * @param h The handler
-     * @param what The event
-     */
-    public void registerForDataEnabledOverrideChanged(Handler h, int what) {
-        mOverallDataEnabledOverrideChangedRegistrants.addUnique(h, what, null);
-        notifyDataEnabledOverrideChanged();
-    }
-
-    /**
-     * Unregistered for data enabled override changed event.
-     *
-     * @param h The handler
-     */
-    public void unregisterForDataEnabledOverrideChanged(Handler h) {
-        mOverallDataEnabledOverrideChangedRegistrants.remove(h);
-    }
-
-    private static boolean isStandAloneOpportunistic(int subId, Context context) {
-        SubscriptionInfo info = SubscriptionController.getInstance().getActiveSubscriptionInfo(
-                subId, context.getOpPackageName(), context.getAttributionTag());
-        return (info != null) && info.isOpportunistic() && info.getGroupUuid() == null;
-    }
-
-    public synchronized boolean isDataEnabled(int apnType) {
-        if (isProvisioning()) {
-            return isProvisioningDataEnabled();
-        } else {
-            boolean userDataEnabled = isUserDataEnabled();
-            // Check if we should temporarily enable data in certain conditions.
-            boolean isDataEnabledOverridden = mDataEnabledOverride
-                    .shouldOverrideDataEnabledSettings(mPhone, apnType);
-
-            return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled
-                    && mThermalDataEnabled && (userDataEnabled || isDataEnabledOverridden));
-        }
-    }
-
-    private void log(String s) {
-        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
-    }
-
-    private void localLog(String name, boolean value) {
-        mSettingChangeLocalLog.log(name + " change to " + value);
-    }
-
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(" DataEnabledSettings=");
-        mSettingChangeLocalLog.dump(fd, pw, args);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
deleted file mode 100644
index d006004..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ /dev/null
@@ -1,978 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.LinkProperties;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RegistrantList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.permission.LegacyPermissionManager;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.AnomalyReporter;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.IDataService;
-import android.telephony.data.IDataServiceCallback;
-import android.telephony.data.NetworkSliceInfo;
-import android.telephony.data.TrafficDescriptor;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConfigurationManager;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Data service manager manages handling data requests and responses on data services (e.g.
- * Cellular data service, IWLAN data service).
- */
-public class DataServiceManager extends Handler {
-    private static final boolean DBG = true;
-
-    static final String DATA_CALL_RESPONSE = "data_call_response";
-
-    private static final int EVENT_BIND_DATA_SERVICE = 1;
-
-    private static final int EVENT_WATCHDOG_TIMEOUT = 2;
-
-    private static final long REQUEST_UNRESPONDED_TIMEOUT = 10 * MINUTE_IN_MILLIS; // 10 mins
-
-    private static final long CHANGE_PERMISSION_TIMEOUT_MS = 15 * SECOND_IN_MILLIS; // 15 secs
-
-    private final Phone mPhone;
-
-    private final String mTag;
-
-    private final CarrierConfigManager mCarrierConfigManager;
-    private final AppOpsManager mAppOps;
-    private final LegacyPermissionManager mPermissionManager;
-
-    private final int mTransportType;
-
-    private boolean mBound;
-
-    private IDataService mIDataService;
-
-    private DataServiceManagerDeathRecipient mDeathRecipient;
-
-    private final RegistrantList mServiceBindingChangedRegistrants = new RegistrantList();
-
-    private final Map<IBinder, Message> mMessageMap = new ConcurrentHashMap<>();
-
-    private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
-
-    private final RegistrantList mApnUnthrottledRegistrants = new RegistrantList();
-
-    private String mTargetBindingPackageName;
-
-    private CellularDataServiceConnection mServiceConnection;
-
-    private final UUID mAnomalyUUID = UUID.fromString("fc1956de-c080-45de-8431-a1faab687110");
-    private String mLastBoundPackageName;
-
-    /**
-     * Helpful for logging
-     * @return the tag name
-     *
-     * @hide
-     */
-    public String getTag() {
-        return mTag;
-    }
-
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
-                    && mPhone.getPhoneId() == intent.getIntExtra(
-                    CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
-                // We should wait for carrier config changed event because the target binding
-                // package name can come from the carrier config. Note that we still get this event
-                // even when SIM is absent.
-                if (DBG) log("Carrier config changed. Try to bind data service.");
-                sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
-            }
-        }
-    };
-
-    private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
-        @Override
-        public void binderDied() {
-            // TODO: try to rebind the service.
-            String message = "Data service " + mLastBoundPackageName +  " for transport type "
-                    + AccessNetworkConstants.transportTypeToString(mTransportType) + " died.";
-            loge(message);
-            AnomalyReporter.reportAnomaly(mAnomalyUUID, message, mPhone.getCarrierId());
-        }
-    }
-
-    private void grantPermissionsToService(String packageName) {
-        final String[] pkgToGrant = {packageName};
-        CountDownLatch latch = new CountDownLatch(1);
-        try {
-            mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
-                    pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run,
-                    isSuccess -> {
-                        if (isSuccess) {
-                            latch.countDown();
-                        } else {
-                            loge("Failed to grant permissions to service.");
-                        }
-                    });
-            TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
-            mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS,
-                    UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
-            mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION,
-                    UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
-        } catch (RuntimeException e) {
-            loge("Binder to package manager died, permission grant for DataService failed.");
-            throw e;
-        }
-    }
-
-    /**
-     * Loop through all DataServices installed on the system and revoke permissions from any that
-     * are not currently the WWAN or WLAN data service.
-     */
-    private void revokePermissionsFromUnusedDataServices() {
-        // Except the current data services from having their permissions removed.
-        Set<String> dataServices = getAllDataServicePackageNames();
-        for (int transportType : mPhone.getAccessNetworksManager().getAvailableTransports()) {
-            dataServices.remove(getDataServicePackageName(transportType));
-        }
-
-        CountDownLatch latch = new CountDownLatch(1);
-        try {
-            String[] dataServicesArray = new String[dataServices.size()];
-            dataServices.toArray(dataServicesArray);
-            mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
-                    dataServicesArray, UserHandle.of(UserHandle.myUserId()), Runnable::run,
-                    isSuccess -> {
-                        if (isSuccess) {
-                            latch.countDown();
-                        } else {
-                            loge("Failed to revoke permissions from data services.");
-                        }
-                    });
-            TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
-            for (String pkg : dataServices) {
-                mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, UserHandle.myUserId(),
-                        pkg, AppOpsManager.MODE_ERRORED);
-                mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION, UserHandle.myUserId(),
-                        pkg, AppOpsManager.MODE_ERRORED);
-            }
-        } catch (RuntimeException e) {
-            loge("Binder to package manager died; failed to revoke DataService permissions.");
-            throw e;
-        }
-    }
-
-    private final class CellularDataServiceConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DBG) log("onServiceConnected");
-            mIDataService = IDataService.Stub.asInterface(service);
-            mDeathRecipient = new DataServiceManagerDeathRecipient();
-            mBound = true;
-            mLastBoundPackageName = getDataServicePackageName();
-            removeMessages(EVENT_WATCHDOG_TIMEOUT);
-
-            try {
-                service.linkToDeath(mDeathRecipient, 0);
-                mIDataService.createDataServiceProvider(mPhone.getPhoneId());
-                mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
-                        new CellularDataServiceCallback("dataCallListChanged"));
-                mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(),
-                        new CellularDataServiceCallback("unthrottleApn"));
-            } catch (RemoteException e) {
-                loge("Remote exception. " + e);
-                return;
-            }
-            mServiceBindingChangedRegistrants.notifyResult(true);
-        }
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DBG) log("onServiceDisconnected");
-            removeMessages(EVENT_WATCHDOG_TIMEOUT);
-            mIDataService = null;
-            mBound = false;
-            mServiceBindingChangedRegistrants.notifyResult(false);
-            mTargetBindingPackageName = null;
-        }
-    }
-
-    private final class CellularDataServiceCallback extends IDataServiceCallback.Stub {
-
-        private final String mTag;
-
-        CellularDataServiceCallback(String tag) {
-            mTag = tag;
-        }
-
-        public String getTag() {
-            return mTag;
-        }
-
-        @Override
-        public void onSetupDataCallComplete(@DataServiceCallback.ResultCode int resultCode,
-                                            DataCallResponse response) {
-            if (DBG) {
-                log("onSetupDataCallComplete. resultCode = " + resultCode + ", response = "
-                        + response);
-            }
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
-            Message msg = mMessageMap.remove(asBinder());
-            if (msg != null) {
-                msg.getData().putParcelable(DATA_CALL_RESPONSE, response);
-                sendCompleteMessage(msg, resultCode);
-            } else {
-                loge("Unable to find the message for setup call response.");
-            }
-        }
-
-        @Override
-        public void onDeactivateDataCallComplete(@DataServiceCallback.ResultCode int resultCode) {
-            if (DBG) log("onDeactivateDataCallComplete. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onSetInitialAttachApnComplete(@DataServiceCallback.ResultCode int resultCode) {
-            if (DBG) log("onSetInitialAttachApnComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onSetDataProfileComplete(@DataServiceCallback.ResultCode int resultCode) {
-            if (DBG) log("onSetDataProfileComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onRequestDataCallListComplete(@DataServiceCallback.ResultCode int resultCode,
-                                              List<DataCallResponse> dataCallList) {
-            if (DBG) log("onRequestDataCallListComplete. resultCode = " + resultCode);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
-            mDataCallListChangedRegistrants.notifyRegistrants(
-                    new AsyncResult(null, dataCallList, null));
-        }
-
-        @Override
-        public void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode) {
-            if (DBG) log("onHandoverStarted. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onHandoverCancelled(@DataServiceCallback.ResultCode int resultCode) {
-            if (DBG) log("onHandoverCancelled. resultCode = " + resultCode);
-            removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
-            Message msg = mMessageMap.remove(asBinder());
-            sendCompleteMessage(msg, resultCode);
-        }
-
-        @Override
-        public void onApnUnthrottled(String apn) {
-            if (apn != null) {
-                mApnUnthrottledRegistrants.notifyRegistrants(
-                        new AsyncResult(null, apn, null));
-            } else {
-                loge("onApnUnthrottled: apn is null");
-            }
-        }
-
-        @Override
-        public void onDataProfileUnthrottled(DataProfile dataProfile) {
-            if (dataProfile != null) {
-                mApnUnthrottledRegistrants.notifyRegistrants(
-                        new AsyncResult(null, dataProfile, null));
-            } else {
-                loge("onDataProfileUnthrottled: dataProfile is null");
-            }
-        }
-    }
-
-    /**
-     * Constructor
-     *
-     * @param phone The phone object
-     * @param transportType The transport type
-     * @param tagSuffix Logging tag suffix
-     */
-    public DataServiceManager(Phone phone, @TransportType int transportType, String tagSuffix) {
-        mPhone = phone;
-        mTag = "DSM" + tagSuffix;
-        mTransportType = transportType;
-        mBound = false;
-        mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
-        // NOTE: Do NOT use AppGlobals to retrieve the permission manager; AppGlobals
-        // caches the service instance, but we need to explicitly request a new service
-        // so it can be mocked out for tests
-        mPermissionManager = (LegacyPermissionManager) phone.getContext().getSystemService(
-                Context.LEGACY_PERMISSION_SERVICE);
-        mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
-
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        try {
-            Context contextAsUser = phone.getContext().createPackageContextAsUser(
-                phone.getContext().getPackageName(), 0, UserHandle.ALL);
-            contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
-                null /* broadcastPermission */, null);
-        } catch (PackageManager.NameNotFoundException e) {
-            loge("Package name not found: " + e.getMessage());
-        }
-
-        PhoneConfigurationManager.registerForMultiSimConfigChange(
-                this, EVENT_BIND_DATA_SERVICE, null);
-
-        sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
-    }
-
-    /**
-     * Handle message events
-     *
-     * @param msg The message to handle
-     */
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case EVENT_BIND_DATA_SERVICE:
-                rebindDataService();
-                break;
-            case EVENT_WATCHDOG_TIMEOUT:
-                handleRequestUnresponded((CellularDataServiceCallback) msg.obj);
-                break;
-            default:
-                loge("Unhandled event " + msg.what);
-        }
-    }
-
-    private void handleRequestUnresponded(CellularDataServiceCallback callback) {
-        String message = "Request " + callback.getTag() + " unresponded on transport "
-                + AccessNetworkConstants.transportTypeToString(mTransportType) + " in "
-                + REQUEST_UNRESPONDED_TIMEOUT / 1000 + " seconds.";
-        log(message);
-        // Using fixed UUID to avoid duplicate bugreport notification
-        AnomalyReporter.reportAnomaly(
-                UUID.fromString("f5d5cbe6-9bd6-4009-b764-42b1b649b1de"),
-                message, mPhone.getCarrierId());
-    }
-
-    private void unbindDataService() {
-        // Start by cleaning up all packages that *shouldn't* have permissions.
-        revokePermissionsFromUnusedDataServices();
-        if (mIDataService != null && mIDataService.asBinder().isBinderAlive()) {
-            log("unbinding service");
-            // Remove the network availability updater and then unbind the service.
-            try {
-                mIDataService.removeDataServiceProvider(mPhone.getPhoneId());
-            } catch (RemoteException e) {
-                loge("Cannot remove data service provider. " + e);
-            }
-        }
-
-        if (mServiceConnection != null) {
-            mPhone.getContext().unbindService(mServiceConnection);
-        }
-        mIDataService = null;
-        mServiceConnection = null;
-        mTargetBindingPackageName = null;
-        mBound = false;
-    }
-
-    private void bindDataService(String packageName) {
-        if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
-            loge("can't bindDataService with invalid phone or phoneId.");
-            return;
-        }
-
-        if (TextUtils.isEmpty(packageName)) {
-            loge("Can't find the binding package");
-            return;
-        }
-
-        Intent intent = null;
-        String className = getDataServiceClassName();
-        if (TextUtils.isEmpty(className)) {
-            intent = new Intent(DataService.SERVICE_INTERFACE);
-            intent.setPackage(packageName);
-        } else {
-            ComponentName cm = new ComponentName(packageName, className);
-            intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
-        }
-
-        // Then pre-emptively grant the permissions to the package we will bind.
-        grantPermissionsToService(packageName);
-
-        try {
-            mServiceConnection = new CellularDataServiceConnection();
-            if (!mPhone.getContext().bindService(
-                    intent, mServiceConnection, Context.BIND_AUTO_CREATE)) {
-                loge("Cannot bind to the data service.");
-                return;
-            }
-            mTargetBindingPackageName = packageName;
-        } catch (Exception e) {
-            loge("Cannot bind to the data service. Exception: " + e);
-        }
-    }
-
-    private void rebindDataService() {
-        String packageName = getDataServicePackageName();
-        // Do nothing if no need to rebind.
-        if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
-                && TextUtils.equals(packageName, mTargetBindingPackageName)) {
-            if (DBG) log("Service " + packageName + " already bound or being bound.");
-            return;
-        }
-
-        unbindDataService();
-        bindDataService(packageName);
-    }
-
-    @NonNull
-    private Set<String> getAllDataServicePackageNames() {
-        // Cowardly using the public PackageManager interface here.
-        // Note: This matches only packages that were installed on the system image. If we ever
-        // expand the permissions model to allow CarrierPrivileged packages, then this will need
-        // to be updated.
-        List<ResolveInfo> dataPackages =
-                mPhone.getContext().getPackageManager().queryIntentServices(
-                        new Intent(DataService.SERVICE_INTERFACE),
-                                PackageManager.MATCH_SYSTEM_ONLY);
-        HashSet<String> packageNames = new HashSet<>();
-        for (ResolveInfo info : dataPackages) {
-            if (info.serviceInfo == null) continue;
-            packageNames.add(info.serviceInfo.packageName);
-        }
-        return packageNames;
-    }
-
-    /**
-     * Get the data service package name for our current transport type.
-     *
-     * @return package name of the data service package for the the current transportType.
-     */
-    public String getDataServicePackageName() {
-        return getDataServicePackageName(mTransportType);
-    }
-
-    /**
-     * Get the data service package by transport type.
-     *
-     * When we bind to a DataService package, we need to revoke permissions from stale
-     * packages; we need to exclude data packages for all transport types, so we need to
-     * to be able to query by transport type.
-     *
-     * @param transportType The transport type
-     * @return package name of the data service package for the specified transportType.
-     */
-    private String getDataServicePackageName(@TransportType int transportType) {
-        String packageName;
-        int resourceId;
-        String carrierConfig;
-
-        switch (transportType) {
-            case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
-                resourceId = com.android.internal.R.string.config_wwan_data_service_package;
-                carrierConfig = CarrierConfigManager
-                        .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
-                break;
-            case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
-                resourceId = com.android.internal.R.string.config_wlan_data_service_package;
-                carrierConfig = CarrierConfigManager
-                        .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
-                break;
-            default:
-                throw new IllegalStateException("Transport type not WWAN or WLAN. type="
-                        + AccessNetworkConstants.transportTypeToString(mTransportType));
-        }
-
-        // Read package name from resource overlay
-        packageName = mPhone.getContext().getResources().getString(resourceId);
-
-        PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
-        if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
-            // If carrier config overrides it, use the one from carrier config
-            packageName = b.getString(carrierConfig, packageName);
-        }
-
-        return packageName;
-    }
-
-    /**
-     * Get the data service class name for our current transport type.
-     *
-     * @return class name of the data service package for the the current transportType.
-     */
-    private String getDataServiceClassName() {
-        return getDataServiceClassName(mTransportType);
-    }
-
-
-    /**
-     * Get the data service class by transport type.
-     *
-     * @param transportType either WWAN or WLAN
-     * @return class name of the data service package for the specified transportType.
-     */
-    private String getDataServiceClassName(int transportType) {
-        String className;
-        int resourceId;
-        String carrierConfig;
-        switch (transportType) {
-            case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
-                resourceId = com.android.internal.R.string.config_wwan_data_service_class;
-                carrierConfig = CarrierConfigManager
-                        .KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING;
-                break;
-            case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
-                resourceId = com.android.internal.R.string.config_wlan_data_service_class;
-                carrierConfig = CarrierConfigManager
-                        .KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING;
-                break;
-            default:
-                throw new IllegalStateException("Transport type not WWAN or WLAN. type="
-                        + transportType);
-        }
-
-        // Read package name from resource overlay
-        className = mPhone.getContext().getResources().getString(resourceId);
-
-        PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
-        if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
-            // If carrier config overrides it, use the one from carrier config
-            className = b.getString(carrierConfig, className);
-        }
-
-        return className;
-    }
-
-    private void sendCompleteMessage(Message msg, @DataServiceCallback.ResultCode int code) {
-        if (msg != null) {
-            msg.arg1 = code;
-            msg.sendToTarget();
-        }
-    }
-
-    /**
-     * Setup a data connection. The data service provider must implement this method to support
-     * establishing a packet data connection. When completed or error, the service must invoke
-     * the provided callback to notify the platform.
-     *
-     * @param accessNetworkType Access network type that the data call will be established on.
-     *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
-     * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
-     * @param isRoaming True if the device is data roaming.
-     * @param allowRoaming True if data roaming is allowed by the user.
-     * @param reason The reason for data setup. Must be {@link DataService#REQUEST_REASON_NORMAL} or
-     *        {@link DataService#REQUEST_REASON_HANDOVER}.
-     * @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this
-     *        is the link properties of the existing data connection, otherwise null.
-     * @param pduSessionId The pdu session id to be used for this data call.  A value of -1 means
-     *                     no pdu session id was attached to this call.
-     *                     Reference: 3GPP TS 24.007 Section 11.2.3.1b
-     * @param sliceInfo The slice that represents S-NSSAI.
-     *                  Reference: 3GPP TS 24.501
-     * @param trafficDescriptor The traffic descriptor for this data call, used for URSP matching.
-     *                          Reference: 3GPP TS TS 24.526 Section 5.2
-     * @param matchAllRuleAllowed True if using the default match-all URSP rule for this request is
-     *                            allowed.
-     * @param onCompleteMessage The result message for this request. Null if the client does not
-     *        care about the result.
-     */
-    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-            boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
-            @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,
-            boolean matchAllRuleAllowed, Message onCompleteMessage) {
-        if (DBG) log("setupDataCall");
-        if (!mBound) {
-            loge("setupDataCall: Data service not bound.");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        CellularDataServiceCallback callback = new CellularDataServiceCallback("setupDataCall");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
-        try {
-            sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
-                    REQUEST_UNRESPONDED_TIMEOUT);
-            mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
-                    isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
-                    trafficDescriptor, matchAllRuleAllowed, callback);
-        } catch (RemoteException e) {
-            loge("setupDataCall: Cannot invoke setupDataCall on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Deactivate a data connection. The data service provider must implement this method to
-     * support data connection tear down. When completed or error, the service must invoke the
-     * provided callback to notify the platform.
-     *
-     * @param cid Call id returned in the callback of {@link #setupDataCall(int, DataProfile,
-     *        boolean, boolean, int, LinkProperties, Message)}
-     * @param reason The reason for data deactivation. Must be
-     *        {@link DataService#REQUEST_REASON_NORMAL}, {@link DataService#REQUEST_REASON_SHUTDOWN}
-     *        or {@link DataService#REQUEST_REASON_HANDOVER}.
-     * @param onCompleteMessage The result message for this request. Null if the client does not
-     *        care about the result.
-     */
-    public void deactivateDataCall(int cid, int reason, Message onCompleteMessage) {
-        if (DBG) log("deactivateDataCall");
-        if (!mBound) {
-            loge("Data service not bound.");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        CellularDataServiceCallback callback =
-                new CellularDataServiceCallback("deactivateDataCall");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
-        try {
-            sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
-                    REQUEST_UNRESPONDED_TIMEOUT);
-            mIDataService.deactivateDataCall(mPhone.getPhoneId(), cid, reason, callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke deactivateDataCall on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Indicates that a handover has begun.  This is called on the source transport.
-     *
-     * Any resources being transferred cannot be released while a
-     * handover is underway.
-     *
-     * If a handover was unsuccessful, then the framework calls DataServiceManager#cancelHandover.
-     * The target transport retains ownership over any of the resources being transferred.
-     *
-     * If a handover was successful, the framework calls DataServiceManager#deactivateDataCall with
-     * reason HANDOVER. The target transport now owns the transferred resources and is
-     * responsible for releasing them.
-     *
-     * @param cid The identifier of the data call which is provided in DataCallResponse
-     * @param onCompleteMessage The result callback for this request.
-     */
-    public void startHandover(int cid, @NonNull Message onCompleteMessage) {
-        CellularDataServiceCallback callback =
-                setupCallbackHelper("startHandover", onCompleteMessage);
-        if (callback == null) {
-            loge("startHandover: callback == null");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        try {
-            sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
-                    REQUEST_UNRESPONDED_TIMEOUT);
-            mIDataService.startHandover(mPhone.getPhoneId(), cid, callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke startHandover on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Indicates that a handover was cancelled after a call to DataServiceManager#startHandover.
-     * This is called on the source transport.
-     *
-     * Since the handover was unsuccessful, the source transport retains ownership over any of
-     * the resources being transferred and is still responsible for releasing them.
-     *
-     * @param cid The identifier of the data call which is provided in DataCallResponse
-     * @param onCompleteMessage The result callback for this request.
-     */
-    public void cancelHandover(int cid, @NonNull Message onCompleteMessage) {
-        CellularDataServiceCallback callback =
-                setupCallbackHelper("cancelHandover", onCompleteMessage);
-        if (callback == null) {
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        try {
-            sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
-                    REQUEST_UNRESPONDED_TIMEOUT);
-            mIDataService.cancelHandover(mPhone.getPhoneId(), cid, callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke cancelHandover on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    @Nullable
-    private CellularDataServiceCallback setupCallbackHelper(
-            @NonNull final String operationName, @NonNull final Message onCompleteMessage) {
-        if (DBG) log(operationName);
-        if (!mBound) {
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return null;
-        }
-
-        CellularDataServiceCallback callback =
-                new CellularDataServiceCallback(operationName);
-        if (onCompleteMessage != null) {
-            if (DBG) log(operationName + ": onCompleteMessage set");
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        } else {
-            if (DBG) log(operationName + ": onCompleteMessage not set");
-        }
-        return callback;
-    }
-
-    /**
-     * Set an APN to initial attach network.
-     *
-     * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
-     * @param isRoaming True if the device is data roaming.
-     * @param onCompleteMessage The result message for this request. Null if the client does not
-     *        care about the result.
-     */
-    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
-                                    Message onCompleteMessage) {
-        if (DBG) log("setInitialAttachApn");
-        if (!mBound) {
-            loge("Data service not bound.");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        CellularDataServiceCallback callback =
-                new CellularDataServiceCallback("setInitialAttachApn");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
-        try {
-            mIDataService.setInitialAttachApn(mPhone.getPhoneId(), dataProfile, isRoaming,
-                    callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke setInitialAttachApn on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Send current carrier's data profiles to the data service for data call setup. This is
-     * only for CDMA carrier that can change the profile through OTA. The data service should
-     * always uses the latest data profile sent by the framework.
-     *
-     * @param dps A list of data profiles.
-     * @param isRoaming True if the device is data roaming.
-     * @param onCompleteMessage The result message for this request. Null if the client does not
-     *        care about the result.
-     */
-    public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
-                               Message onCompleteMessage) {
-        if (DBG) log("setDataProfile");
-        if (!mBound) {
-            loge("Data service not bound.");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        CellularDataServiceCallback callback = new CellularDataServiceCallback("setDataProfile");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
-        try {
-            mIDataService.setDataProfile(mPhone.getPhoneId(), dps, isRoaming, callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke setDataProfile on data service.");
-            mMessageMap.remove(callback.asBinder());
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Get the active data call list.
-     *
-     * @param onCompleteMessage The result message for this request. Null if the client does not
-     *        care about the result.
-     */
-    public void requestDataCallList(Message onCompleteMessage) {
-        if (DBG) log("requestDataCallList");
-        if (!mBound) {
-            loge("Data service not bound.");
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-            return;
-        }
-
-        CellularDataServiceCallback callback =
-                new CellularDataServiceCallback("requestDataCallList");
-        if (onCompleteMessage != null) {
-            mMessageMap.put(callback.asBinder(), onCompleteMessage);
-        }
-        try {
-            mIDataService.requestDataCallList(mPhone.getPhoneId(), callback);
-        } catch (RemoteException e) {
-            loge("Cannot invoke requestDataCallList on data service.");
-            if (callback != null) {
-                mMessageMap.remove(callback.asBinder());
-            }
-            sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
-        }
-    }
-
-    /**
-     * Register for data call list changed event.
-     *
-     * @param h The target to post the event message to.
-     * @param what The event.
-     */
-    public void registerForDataCallListChanged(Handler h, int what) {
-        if (h != null) {
-            mDataCallListChangedRegistrants.addUnique(h, what, null);
-        }
-    }
-
-    /**
-     * Unregister for data call list changed event.
-     *
-     * @param h The handler
-     */
-    public void unregisterForDataCallListChanged(Handler h) {
-        if (h != null) {
-            mDataCallListChangedRegistrants.remove(h);
-        }
-    }
-
-    /**
-     * Register apn unthrottled event
-     *
-     * @param h The target to post the event message to.
-     * @param what The event.
-     */
-    public void registerForApnUnthrottled(Handler h, int what) {
-        if (h != null) {
-            mApnUnthrottledRegistrants.addUnique(h, what, null);
-        }
-    }
-
-    /**
-     * Unregister for apn unthrottled event
-     *
-     * @param h The handler
-     */
-    public void unregisterForApnUnthrottled(Handler h) {
-        if (h != null) {
-            mApnUnthrottledRegistrants.remove(h);
-        }
-    }
-
-    /**
-     * Register for data service binding status changed event.
-     *
-     * @param h The target to post the event message to.
-     * @param what The event.
-     * @param obj The user object.
-     */
-    public void registerForServiceBindingChanged(Handler h, int what, Object obj) {
-        if (h != null) {
-            mServiceBindingChangedRegistrants.addUnique(h, what, obj);
-        }
-
-    }
-
-    /**
-     * Unregister for data service binding status changed event.
-     *
-     * @param h The handler
-     */
-    public void unregisterForServiceBindingChanged(Handler h) {
-        if (h != null) {
-            mServiceBindingChangedRegistrants.remove(h);
-        }
-    }
-
-    /**
-     * Get the transport type. Must be a {@link TransportType}.
-     *
-     * @return
-     */
-    @TransportType
-    public int getTransportType() {
-        return mTransportType;
-    }
-
-    private void log(String s) {
-        Rlog.d(mTag, s);
-    }
-
-    private void loge(String s) {
-        Rlog.e(mTag, s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
deleted file mode 100644
index 4a465d2..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.ElapsedRealtimeLong;
-import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.ThrottleStatus;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.telephony.Rlog;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Data throttler tracks the throttling status of the data network and notifies registrants when
- * there are changes.  The throttler is per phone and per transport type.
- */
-public class DataThrottler extends Handler {
-    private static final String TAG = DataThrottler.class.getSimpleName();
-
-    private static final int EVENT_SET_RETRY_TIME = 1;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED = 2;
-    private static final int EVENT_RESET = 3;
-    private static final int EVENT_AIRPLANE_MODE_CHANGED = 4;
-    private static final int EVENT_TRACING_AREA_CODE_CHANGED = 5;
-
-    private final Phone mPhone;
-    private final int mSlotIndex;
-    private final @AccessNetworkConstants.TransportType int mTransportType;
-    private boolean mResetWhenAreaCodeChanged = false;
-
-    /**
-     * Callbacks that report the apn throttle status.
-     */
-    private final List<DataThrottler.Callback> mCallbacks = new ArrayList<>();
-
-    /**
-     * Keeps track of detailed information of the throttle status that is meant to be
-     * reported to other components.
-     */
-    private final Map<Integer, ThrottleStatus> mThrottleStatus = new ConcurrentHashMap<>();
-
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
-                    if (intent.getBooleanExtra(
-                            CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
-                        // Ignore the rebroadcast one to prevent multiple carrier config changed
-                        // event during boot up.
-                        return;
-                    }
-                    int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
-                            SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                    if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                        sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
-                    }
-                }
-            }
-        }
-    };
-
-    public DataThrottler(Phone phone, int transportType) {
-        super(null, false);
-        mPhone = phone;
-        mSlotIndex = phone.getPhoneId();
-        mTransportType = transportType;
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
-
-        mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this,
-                EVENT_AIRPLANE_MODE_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForAreaCodeChanged(this,
-                EVENT_TRACING_AREA_CODE_CHANGED, null);
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        AsyncResult ar;
-        switch (msg.what) {
-            case EVENT_SET_RETRY_TIME:
-                int apnTypes = msg.arg1;
-                int newRequestType = msg.arg2;
-                long retryElapsedTime = (long) msg.obj;
-                setRetryTimeInternal(apnTypes, retryElapsedTime, newRequestType);
-                break;
-            case EVENT_CARRIER_CONFIG_CHANGED:
-                onCarrierConfigChanged();
-                break;
-            case EVENT_RESET:
-                resetInternal();
-                break;
-            case EVENT_AIRPLANE_MODE_CHANGED:
-                ar = (AsyncResult) msg.obj;
-                if (!(Boolean) ar.result) {
-                    resetInternal();
-                }
-                break;
-            case EVENT_TRACING_AREA_CODE_CHANGED:
-                if (mResetWhenAreaCodeChanged) {
-                    resetInternal();
-                }
-                break;
-        }
-    }
-
-    @NonNull
-    private PersistableBundle getCarrierConfig() {
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            // If an invalid subId is used, this bundle will contain default values.
-            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
-            if (config != null) {
-                return config;
-            }
-        }
-        // Return static default defined in CarrierConfigManager.
-        return CarrierConfigManager.getDefaultConfig();
-    }
-
-    private void onCarrierConfigChanged() {
-        PersistableBundle config = getCarrierConfig();
-        mResetWhenAreaCodeChanged = config.getBoolean(
-                CarrierConfigManager.KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
-    }
-
-    /**
-     * Set the retry time and handover failure mode for the give APN types.
-     *
-     * @param apnTypes APN types
-     * @param retryElapsedTime The elapsed time that data connection for APN types should not be
-     * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
-     * {@link RetryManager#NO_RETRY} indicates retry should never happen.
-     */
-    public void setRetryTime(@ApnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime,
-            @DcTracker.RequestNetworkType int newRequestType) {
-        sendMessage(obtainMessage(EVENT_SET_RETRY_TIME, apnTypes, newRequestType,
-                retryElapsedTime));
-    }
-
-    /**
-     * Set the retry time and handover failure mode for the give APN types. This is running on the
-     * handler thread.
-     *
-     * @param apnTypes APN types
-     * @param retryElapsedTime The elapsed time that data connection for APN types should not be
-     * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
-     * {@link RetryManager#NO_RETRY} indicates retry should never happen.
-     */
-    private void setRetryTimeInternal(@ApnType int apnTypes, @ElapsedRealtimeLong
-            long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType) {
-        if (retryElapsedTime < 0) {
-            retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
-        }
-
-        List<ThrottleStatus> changedStatuses = new ArrayList<>();
-        while (apnTypes != 0) {
-            int apnType;
-            // Due to an API mistake of ApnSetting.TYPE_DEFAULT (which combines default and hipri 
-            // bit), we need to do special handling here.
-            if ((apnTypes & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
-                apnType = ApnSetting.TYPE_DEFAULT;
-                apnTypes &= ~ApnSetting.TYPE_DEFAULT;
-            } else {
-                //Extract the least significant bit.
-                apnType = apnTypes & -apnTypes;
-                //Remove the least significant bit.
-                apnTypes &= apnTypes - 1;
-            }
-
-            //Update the apn throttle status
-            ThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType);
-
-            ThrottleStatus oldStatus = mThrottleStatus.get(apnType);
-
-            //Check to see if there is a change that needs to be applied
-            if (!newStatus.equals(oldStatus)) {
-                //Mark as changed status
-                changedStatuses.add(newStatus);
-
-                //Put the new status in the temp space
-                mThrottleStatus.put(apnType, newStatus);
-            }
-        }
-
-        if (changedStatuses.size() > 0) {
-            sendThrottleStatusChanged(changedStatuses);
-        }
-    }
-
-    /**
-     * Get the earliest retry time for the given APN type. The time is the system's elapse time.
-     *
-     * Note the DataThrottler is running phone process's main thread, which is most of the telephony
-     * components running on. Calling this method from other threads might run into race conditions.
-     *
-     * @param apnType APN type
-     * @return The earliest retry time for APN type. The time is the system's elapse time.
-     * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN
-     * type, {@link RetryManager#NO_RETRY} indicates retry should never happen.
-     */
-    @ElapsedRealtimeLong
-    public long getRetryTime(@ApnType int apnType) {
-        ThrottleStatus status = mThrottleStatus.get(apnType);
-        if (status != null) {
-            if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) {
-                return RetryManager.NO_SUGGESTED_RETRY_DELAY;
-            } else {
-                return status.getThrottleExpiryTimeMillis();
-            }
-        }
-        return RetryManager.NO_SUGGESTED_RETRY_DELAY;
-    }
-
-    /**
-     * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
-     */
-    public void reset() {
-        sendEmptyMessage(EVENT_RESET);
-    }
-
-    /**
-     * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
-     */
-    private void resetInternal() {
-        final List<Integer> apnTypes = new ArrayList<>();
-        for (ThrottleStatus throttleStatus : mThrottleStatus.values()) {
-            apnTypes.add(throttleStatus.getApnType());
-        }
-
-        for (int apnType : apnTypes) {
-            setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY,
-                    DcTracker.REQUEST_TYPE_NORMAL);
-        }
-    }
-
-    private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime,
-            @DcTracker.RequestNetworkType int newRequestType) {
-        ThrottleStatus.Builder builder = new ThrottleStatus.Builder();
-
-        if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) {
-            builder
-                    .setNoThrottle()
-                    .setRetryType(getRetryType(newRequestType));
-        } else if (retryElapsedTime == RetryManager.NO_RETRY) {
-            builder
-                    .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
-                    .setRetryType(ThrottleStatus.RETRY_TYPE_NONE);
-        } else {
-            builder
-                    .setThrottleExpiryTimeMillis(retryElapsedTime)
-                    .setRetryType(getRetryType(newRequestType));
-        }
-        return builder
-                .setSlotIndex(mSlotIndex)
-                .setTransportType(mTransportType)
-                .setApnType(apnType)
-                .build();
-    }
-
-    private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) {
-        if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) {
-            return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
-        }
-
-        if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
-            return  ThrottleStatus.RETRY_TYPE_HANDOVER;
-        }
-
-        loge("createStatus: Unknown requestType=" + newRequestType);
-        return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
-    }
-
-    private void sendThrottleStatusChanged(List<ThrottleStatus> statuses) {
-        synchronized (mCallbacks) {
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                mCallbacks.get(i).onThrottleStatusChanged(statuses);
-            }
-        }
-    }
-
-    private static void loge(String s) {
-        Rlog.e(TAG, s);
-    }
-
-    /**
-     * Reports changes to apn throttle statuses.
-     *
-     * Note: All statuses are sent when first registered.
-     *
-     * @param callback status changes callback
-     */
-    public void registerForThrottleStatusChanges(DataThrottler.Callback callback) {
-        synchronized (mCallbacks) {
-            //Only add if it's not there already
-            if (!mCallbacks.contains(callback)) {
-                //Report everything the first time
-                List<ThrottleStatus> throttleStatuses =
-                        new ArrayList<>(mThrottleStatus.values());
-                callback.onThrottleStatusChanged(throttleStatuses);
-                mCallbacks.add(callback);
-            }
-        }
-    }
-
-    /**
-     * Unregister the callback
-     * @param callback the callback to unregister
-     */
-    public void unregisterForThrottleStatusChanges(DataThrottler.Callback callback) {
-        synchronized (mCallbacks) {
-            mCallbacks.remove(callback);
-        }
-    }
-
-    /**
-     * Callback for when throttle statuses change
-     */
-    public interface Callback {
-        /**
-         * Called whenever the throttle status of an APN has changed.
-         *
-         * Note: Called with all statuses when first registered.
-         *
-         * @param throttleStatuses the status changes
-         */
-        void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
deleted file mode 100644
index c34157e..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.hardware.radio.V1_4.DataConnActiveStatus;
-import android.net.LinkAddress;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.TrafficDescriptor;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.net.module.util.LinkPropertiesUtils;
-import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
-import com.android.net.module.util.NetUtils;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Data Connection Controller which is a package visible class and controls
- * multiple data connections. For instance listening for unsolicited messages
- * and then demultiplexing them to the appropriate DC.
- */
-public class DcController extends Handler {
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private final Phone mPhone;
-    private final DcTracker mDct;
-    private final String mTag;
-    private final DataServiceManager mDataServiceManager;
-    private final DcTesterDeactivateAll mDcTesterDeactivateAll;
-
-    // package as its used by Testing code
-    // @GuardedBy("mDcListAll")
-    final ArrayList<DataConnection> mDcListAll = new ArrayList<>();
-    // @GuardedBy("mDcListAll")
-    private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>();
-    // @GuardedBy("mTrafficDescriptorsByCid")
-    private final HashMap<Integer, List<TrafficDescriptor>> mTrafficDescriptorsByCid =
-            new HashMap<>();
-
-    /**
-     * Aggregated physical link status from all data connections. This reflects the device's RRC
-     * connection state.
-     * If {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
-     * then This reflects "internet data connection" instead of RRC state.
-     */
-    private @DataCallResponse.LinkStatus int mPhysicalLinkStatus =
-            DataCallResponse.LINK_STATUS_UNKNOWN;
-
-    private RegistrantList mPhysicalLinkStatusChangedRegistrants = new RegistrantList();
-
-    /**
-     * Constructor.
-     *
-     * @param name to be used for the Controller
-     * @param phone the phone associated with Dcc and Dct
-     * @param dct the DataConnectionTracker associated with Dcc
-     * @param dataServiceManager the data service manager that manages data services
-     * @param looper looper for this handler
-     */
-    private DcController(String name, Phone phone, DcTracker dct,
-                         DataServiceManager dataServiceManager, Looper looper) {
-        super(looper);
-        mPhone = phone;
-        mDct = dct;
-        mTag = name;
-        mDataServiceManager = dataServiceManager;
-
-        mDcTesterDeactivateAll = (TelephonyUtils.IS_DEBUGGABLE)
-                ? new DcTesterDeactivateAll(mPhone, DcController.this, this)
-                : null;
-        mDataServiceManager.registerForDataCallListChanged(this,
-                DataConnection.EVENT_DATA_STATE_CHANGED);
-    }
-
-    public static DcController makeDcc(Phone phone, DcTracker dct,
-                                       DataServiceManager dataServiceManager, Looper looper,
-                                       String tagSuffix) {
-        return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, looper);
-    }
-
-    void addDc(DataConnection dc) {
-        synchronized (mDcListAll) {
-            mDcListAll.add(dc);
-        }
-    }
-
-    void removeDc(DataConnection dc) {
-        synchronized (mDcListAll) {
-            mDcListActiveByCid.remove(dc.mCid);
-            mDcListAll.remove(dc);
-        }
-        synchronized (mTrafficDescriptorsByCid) {
-            mTrafficDescriptorsByCid.remove(dc.mCid);
-        }
-    }
-
-    public void addActiveDcByCid(DataConnection dc) {
-        if (DBG && dc.mCid < 0) {
-            log("addActiveDcByCid dc.mCid < 0 dc=" + dc);
-        }
-        synchronized (mDcListAll) {
-            mDcListActiveByCid.put(dc.mCid, dc);
-        }
-        updateTrafficDescriptorsForCid(dc.mCid, dc.getTrafficDescriptors());
-    }
-
-    DataConnection getActiveDcByCid(int cid) {
-        synchronized (mDcListAll) {
-            return mDcListActiveByCid.get(cid);
-        }
-    }
-
-    void removeActiveDcByCid(DataConnection dc) {
-        synchronized (mDcListAll) {
-            DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid);
-            if (DBG && removedDc == null) {
-                log("removeActiveDcByCid removedDc=null dc=" + dc);
-            }
-        }
-        synchronized (mTrafficDescriptorsByCid) {
-            mTrafficDescriptorsByCid.remove(dc.mCid);
-        }
-    }
-
-    boolean isDefaultDataActive() {
-        synchronized (mDcListAll) {
-            return mDcListActiveByCid.values().stream()
-                    .anyMatch(dc -> dc.getApnContexts().stream()
-                            .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
-        }
-    }
-
-    List<TrafficDescriptor> getTrafficDescriptorsForCid(int cid) {
-        synchronized (mTrafficDescriptorsByCid) {
-            return mTrafficDescriptorsByCid.get(cid);
-        }
-    }
-
-    void updateTrafficDescriptorsForCid(int cid, List<TrafficDescriptor> tds) {
-        synchronized (mTrafficDescriptorsByCid) {
-            mTrafficDescriptorsByCid.put(cid, tds);
-        }
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        AsyncResult ar;
-
-        switch (msg.what) {
-            case DataConnection.EVENT_DATA_STATE_CHANGED:
-                ar = (AsyncResult) msg.obj;
-                if (ar.exception == null) {
-                    onDataStateChanged((ArrayList<DataCallResponse>) ar.result);
-                } else {
-                    log("EVENT_DATA_STATE_CHANGED: exception; likely radio not available, ignore");
-                }
-                break;
-            default:
-                loge("Unexpected event " + msg);
-                break;
-        }
-    }
-
-    /**
-     * Process the new list of "known" Data Calls
-     * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
-     */
-    private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
-        final HashMap<Integer, DataConnection> dcListActiveByCid;
-        synchronized (mDcListAll) {
-            dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
-        }
-
-        if (DBG) {
-            log("onDataStateChanged: dcsList=" + dcsList
-                    + " dcListActiveByCid=" + dcListActiveByCid);
-        }
-
-        // Create hashmap of cid to DataCallResponse
-        HashMap<Integer, DataCallResponse> dataCallResponseListByCid = new HashMap<>();
-        for (DataCallResponse dcs : dcsList) {
-            dataCallResponseListByCid.put(dcs.getId(), dcs);
-        }
-
-        // Add a DC that is active but not in the dcsList to the list of DC's to retry
-        ArrayList<DataConnection> dcsToRetry = new ArrayList<>();
-        for (DataConnection dc : dcListActiveByCid.values()) {
-            DataCallResponse response = dataCallResponseListByCid.get(dc.mCid);
-            if (response == null) {
-                if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
-                dcsToRetry.add(dc);
-            } else {
-                List<TrafficDescriptor> oldTds = getTrafficDescriptorsForCid(dc.mCid);
-                List<TrafficDescriptor> newTds = response.getTrafficDescriptors();
-                if (!oldTds.equals(newTds)) {
-                    if (DBG) {
-                        log("onDataStateChanged: add to retry due to TD changed dc=" + dc
-                                + ", oldTds=" + oldTds + ", newTds=" + newTds);
-                    }
-                    updateTrafficDescriptorsForCid(dc.mCid, newTds);
-                    dcsToRetry.add(dc);
-                }
-            }
-        }
-        if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
-
-        // Find which connections have changed state and send a notification or cleanup
-        // and any that are in active need to be retried.
-        ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
-
-        boolean isAnyDataCallDormant = false;
-        boolean isAnyDataCallActive = false;
-        boolean isInternetDataCallActive = false;
-
-        for (DataCallResponse newState : dcsList) {
-
-            DataConnection dc = dcListActiveByCid.get(newState.getId());
-            if (dc == null) {
-                // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
-                loge("onDataStateChanged: no associated DC yet, ignore");
-                continue;
-            }
-
-            List<ApnContext> apnContexts = dc.getApnContexts();
-            if (apnContexts.size() == 0) {
-                if (DBG) loge("onDataStateChanged: no connected apns, ignore");
-            } else {
-                // Determine if the connection/apnContext should be cleaned up
-                // or just a notification should be sent out.
-                if (DBG) {
-                    log("onDataStateChanged: Found ConnId=" + newState.getId()
-                            + " newState=" + newState.toString());
-                }
-                if (apnContexts.stream().anyMatch(
-                        i -> ApnSetting.TYPE_DEFAULT_STRING.equals(i.getApnType()))
-                        && newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
-                    isInternetDataCallActive = true;
-                }
-                if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) {
-                    if (mDct.isCleanupRequired.get()) {
-                        apnsToCleanup.addAll(apnContexts);
-                        mDct.isCleanupRequired.set(false);
-                    } else {
-                        int failCause = DataFailCause.getFailCause(newState.getCause());
-                        if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause,
-                                    mPhone.getSubId())) {
-                            if (DBG) {
-                                log("onDataStateChanged: X restart radio, failCause="
-                                        + failCause);
-                            }
-                            mDct.sendRestartRadio();
-                        } else if (mDct.isPermanentFailure(failCause)) {
-                            if (DBG) {
-                                log("onDataStateChanged: inactive, add to cleanup list. "
-                                        + "failCause=" + failCause);
-                            }
-                            apnsToCleanup.addAll(apnContexts);
-                        } else {
-                            if (DBG) {
-                                log("onDataStateChanged: inactive, add to retry list. "
-                                        + "failCause=" + failCause);
-                            }
-                            dcsToRetry.add(dc);
-                        }
-                    }
-                } else {
-                    // Update the pdu session id
-                    dc.setPduSessionId(newState.getPduSessionId());
-
-                    dc.updatePcscfAddr(newState);
-
-                    // Its active so update the DataConnections link properties
-                    UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
-                    dc.updateResponseFields(newState);
-                    if (result.oldLp.equals(result.newLp)) {
-                        if (DBG) log("onDataStateChanged: no change");
-                    } else {
-                        if (LinkPropertiesUtils.isIdenticalInterfaceName(
-                                result.oldLp, result.newLp)) {
-                            if (!LinkPropertiesUtils.isIdenticalDnses(
-                                    result.oldLp, result.newLp)
-                                    || !LinkPropertiesUtils.isIdenticalRoutes(
-                                            result.oldLp, result.newLp)
-                                    || !LinkPropertiesUtils.isIdenticalHttpProxy(
-                                            result.oldLp, result.newLp)
-                                    || !LinkPropertiesUtils.isIdenticalAddresses(
-                                            result.oldLp, result.newLp)) {
-                                // If the same address type was removed and
-                                // added we need to cleanup
-                                CompareOrUpdateResult<Integer, LinkAddress> car
-                                    = new CompareOrUpdateResult(
-                                  result.oldLp != null ?
-                                    result.oldLp.getLinkAddresses() : null,
-                                  result.newLp != null ?
-                                    result.newLp.getLinkAddresses() : null,
-                                  (la) -> Objects.hash(((LinkAddress)la).getAddress(),
-                                                       ((LinkAddress)la).getPrefixLength(),
-                                                       ((LinkAddress)la).getScope()));
-                                if (DBG) {
-                                    log("onDataStateChanged: oldLp=" + result.oldLp
-                                            + " newLp=" + result.newLp + " car=" + car);
-                                }
-                                boolean needToClean = false;
-                                for (LinkAddress added : car.added) {
-                                    for (LinkAddress removed : car.removed) {
-                                        if (NetUtils.addressTypeMatches(
-                                                removed.getAddress(),
-                                                added.getAddress())) {
-                                            needToClean = true;
-                                            break;
-                                        }
-                                    }
-                                }
-                                if (needToClean) {
-                                    if (DBG) {
-                                        log("onDataStateChanged: addr change,"
-                                                + " cleanup apns=" + apnContexts
-                                                + " oldLp=" + result.oldLp
-                                                + " newLp=" + result.newLp);
-                                    }
-                                    apnsToCleanup.addAll(apnContexts);
-                                }
-                            } else {
-                                if (DBG) {
-                                    log("onDataStateChanged: no changes");
-                                }
-                            }
-                        } else {
-                            apnsToCleanup.addAll(apnContexts);
-                            if (DBG) {
-                                log("onDataStateChanged: interface change, cleanup apns="
-                                        + apnContexts);
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
-                isAnyDataCallActive = true;
-            }
-            if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) {
-                isAnyDataCallDormant = true;
-            }
-        }
-
-        if (mDataServiceManager.getTransportType()
-                == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-            boolean isPhysicalLinkStatusFocusingOnInternetData =
-                    mDct.getLteEndcUsingUserDataForIdleDetection();
-            int physicalLinkStatus =
-                    (isPhysicalLinkStatusFocusingOnInternetData
-                            ? isInternetDataCallActive : isAnyDataCallActive)
-                            ? DataCallResponse.LINK_STATUS_ACTIVE
-                            : DataCallResponse.LINK_STATUS_DORMANT;
-            if (mPhysicalLinkStatus != physicalLinkStatus) {
-                mPhysicalLinkStatus = physicalLinkStatus;
-                mPhysicalLinkStatusChangedRegistrants.notifyResult(mPhysicalLinkStatus);
-            }
-            if (isAnyDataCallDormant && !isAnyDataCallActive) {
-                // There is no way to indicate link activity per APN right now. So
-                // Link Activity will be considered dormant only when all data calls
-                // are dormant.
-                // If a single data call is in dormant state and none of the data
-                // calls are active broadcast overall link status as dormant.
-                if (DBG) {
-                    log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll");
-                }
-                mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
-            } else {
-                if (DBG) {
-                    log("onDataStateChanged: Data Activity updated to NONE. "
-                            + "isAnyDataCallActive = " + isAnyDataCallActive
-                            + " isAnyDataCallDormant = " + isAnyDataCallDormant);
-                }
-                if (isAnyDataCallActive) {
-                    mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
-                }
-            }
-        }
-
-        if (DBG) {
-            log("onDataStateChanged: dcsToRetry=" + dcsToRetry
-                    + " apnsToCleanup=" + apnsToCleanup);
-        }
-
-        // Cleanup connections that have changed
-        for (ApnContext apnContext : apnsToCleanup) {
-            mDct.cleanUpConnection(apnContext);
-        }
-
-        // Retry connections that have disappeared
-        for (DataConnection dc : dcsToRetry) {
-            if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
-            dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
-        }
-
-        if (VDBG) log("onDataStateChanged: X");
-    }
-
-    /**
-     * Register for physical link status (i.e. RRC state) changed event.
-     * if {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
-     * then physical link status is focusing on "internet data connection" instead of RRC state.
-     * @param h The handler
-     * @param what The event
-     */
-    @VisibleForTesting
-    public void registerForPhysicalLinkStatusChanged(Handler h, int what) {
-        mPhysicalLinkStatusChangedRegistrants.addUnique(h, what, null);
-    }
-
-    /**
-     * Unregister from physical link status (i.e. RRC state) changed event.
-     *
-     * @param h The previously registered handler
-     */
-    void unregisterForPhysicalLinkStatusChanged(Handler h) {
-        mPhysicalLinkStatusChangedRegistrants.remove(h);
-    }
-
-    private void log(String s) {
-        Rlog.d(mTag, s);
-    }
-
-    private void loge(String s) {
-        Rlog.e(mTag, s);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        synchronized (mDcListAll) {
-            sb.append("mDcListAll=").append(mDcListAll)
-                    .append(" mDcListActiveByCid=").append(mDcListActiveByCid);
-        }
-        synchronized (mTrafficDescriptorsByCid) {
-            sb.append("mTrafficDescriptorsByCid=").append(mTrafficDescriptorsByCid);
-        }
-        return sb.toString();
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(" mPhone=" + mPhone);
-        synchronized (mDcListAll) {
-            pw.println(" mDcListAll=" + mDcListAll);
-            pw.println(" mDcListActiveByCid=" + mDcListActiveByCid);
-        }
-        synchronized (mTrafficDescriptorsByCid) {
-            pw.println(" mTrafficDescriptorsByCid=" + mTrafficDescriptorsByCid);
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
deleted file mode 100644
index 3cdd209..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.Intent;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.DataFailCause;
-
-import com.android.telephony.Rlog;
-
-/**
- * A package visible class for supporting testing failing bringUp commands. This
- * saves the parameters from a action_fail_bringup intent. See
- * {@link DataConnection#doOnConnect} and {@see DcTesterFailBringUpAll} for more info.
- */
-public class DcFailBringUp {
-    private static final String LOG_TAG = "DcFailBringUp";
-    private static final boolean DBG = true;
-
-    static final String INTENT_BASE = DataConnection.class.getPackage().getName();
-
-    static final String ACTION_FAIL_BRINGUP = "action_fail_bringup";
-
-    // counter with its --ei option name and default value
-    static final String COUNTER = "counter";
-    static final int DEFAULT_COUNTER = 2;
-    int mCounter;
-
-    // failCause with its --ei option name and default value
-    static final String FAIL_CAUSE = "fail_cause";
-    static final int DEFAULT_FAIL_CAUSE = DataFailCause.ERROR_UNSPECIFIED;
-    @DataFailureCause
-    int mFailCause;
-
-    // suggestedRetryTime with its --ei option name and default value
-    static final String SUGGESTED_RETRY_TIME = "suggested_retry_time";
-    static final long DEFAULT_SUGGESTED_RETRY_TIME = -1;
-    long mSuggestedRetryTime;
-
-    // Get the Extra Intent parameters
-    void saveParameters(Intent intent, String s) {
-        if (DBG) log(s + ".saveParameters: action=" + intent.getAction());
-        mCounter = intent.getIntExtra(COUNTER, DEFAULT_COUNTER);
-        mFailCause = DataFailCause.getFailCause(
-                intent.getIntExtra(FAIL_CAUSE, DEFAULT_FAIL_CAUSE));
-        mSuggestedRetryTime =
-                intent.getLongExtra(SUGGESTED_RETRY_TIME, DEFAULT_SUGGESTED_RETRY_TIME);
-        if (DBG) {
-            log(s + ".saveParameters: " + this);
-        }
-    }
-
-    public void saveParameters(int counter, @DataFailureCause int failCause,
-                               long suggestedRetryTime) {
-        mCounter = counter;
-        mFailCause = DataFailCause.getFailCause(failCause);
-        mSuggestedRetryTime = suggestedRetryTime;
-    }
-
-    @Override
-    public String toString() {
-        return "{mCounter=" + mCounter +
-                " mFailCause=" + mFailCause +
-                " mSuggestedRetryTime=" + mSuggestedRetryTime + "}";
-
-    }
-
-    private static void log(String s) {
-        Rlog.d(LOG_TAG, s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
deleted file mode 100644
index c6def34..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.KeepalivePacketData;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketData;
-import android.net.NetworkAgent;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkCapabilities;
-import android.net.NetworkProvider;
-import android.net.QosFilter;
-import android.net.QosSessionAttributes;
-import android.net.SocketKeepalive;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.AnomalyReporter;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
-import android.telephony.data.QosBearerSession;
-import android.util.LocalLog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.SlidingWindowEventCounter;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.data.NotifyQosSessionInterface;
-import com.android.internal.telephony.data.QosCallbackTracker;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class represents a network agent which is communication channel between
- * {@link DataConnection} and {@link com.android.server.ConnectivityService}. The agent is
- * created when data connection enters {@link DataConnection.DcActiveState} until it exits that
- * state.
- *
- * Note that in IWLAN handover scenario, this agent could be transferred to the new
- * {@link DataConnection} so for a short window of time this object might be accessed by two
- * different {@link DataConnection}. Thus each method in this class needs to be synchronized.
- */
-public class DcNetworkAgent extends NetworkAgent implements NotifyQosSessionInterface {
-    private final String mTag;
-
-    private final int mId;
-
-    private final Phone mPhone;
-
-    private final Handler mHandler;
-
-    private int mTransportType;
-
-    private NetworkCapabilities mNetworkCapabilities;
-
-    public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
-
-    private final QosCallbackTracker mQosCallbackTracker;
-
-    private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor();
-
-    private DataConnection mDataConnection;
-
-    private final LocalLog mNetCapsLocalLog = new LocalLog(32);
-
-    // For interface duplicate detection. Key is the net id, value is the interface name in string.
-    private static Map<Integer, String> sInterfaceNames = new ConcurrentHashMap<>();
-
-    private static final long NETWORK_UNWANTED_ANOMALY_WINDOW_MS = TimeUnit.MINUTES.toMillis(5);
-    private static final int NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES =  12;
-
-    private static final int EVENT_UNWANTED_TIMEOUT = 1;
-
-    @VisibleForTesting
-    public DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config,
-            NetworkProvider networkProvider, int transportType) {
-        super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent",
-                dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config,
-                networkProvider);
-        register();
-        mId = getNetwork().getNetId();
-        mTag = "DcNetworkAgent" + "-" + mId;
-        mPhone = phone;
-        mHandler = new Handler(dc.getHandler().getLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-                if (msg.what == EVENT_UNWANTED_TIMEOUT) {
-                    loge("onNetworkUnwanted timed out. Perform silent de-register.");
-                    logd("Unregister from connectivity service. " + sInterfaceNames.get(mId)
-                            + " removed.");
-                    sInterfaceNames.remove(mId);
-                    DcNetworkAgent.this.unregister();
-                }
-            }
-        };
-        mNetworkCapabilities = dc.getNetworkCapabilities();
-        mTransportType = transportType;
-        mDataConnection = dc;
-        if (dc.getLinkProperties() != null) {
-            checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName());
-            logd("created for data connection " + dc.getName() + ", "
-                    + dc.getLinkProperties().getInterfaceName());
-        } else {
-            loge("The connection does not have a valid link properties.");
-        }
-        mQosCallbackTracker = new QosCallbackTracker(this, mPhone);
-    }
-
-    private @NetworkType int getNetworkType() {
-        ServiceState ss = mPhone.getServiceState();
-        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-
-        NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
-        if (nri != null) {
-            networkType = nri.getAccessNetworkTechnology();
-        }
-
-        return networkType;
-    }
-
-    private void checkDuplicateInterface(int netId, @Nullable String interfaceName) {
-        for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) {
-            if (Objects.equals(interfaceName, entry.getValue())) {
-                String message = "Duplicate interface " + interfaceName
-                        + " is detected. DcNetworkAgent-" + entry.getKey()
-                        + " already used this interface name.";
-                loge(message);
-                // Using fixed UUID to avoid duplicate bugreport notification
-                AnomalyReporter.reportAnomaly(
-                        UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
-                        message, mPhone.getCarrierId());
-                return;
-            }
-        }
-        sInterfaceNames.put(netId, interfaceName);
-    }
-
-    /**
-     * @return The tag
-     */
-    String getTag() {
-        return mTag;
-    }
-
-    /**
-     * Set the data connection that owns this network agent.
-     *
-     * @param dc Data connection owning this network agent.
-     * @param transportType Transport that this data connection is on.
-     */
-    public synchronized void acquireOwnership(@NonNull DataConnection dc,
-                                              @TransportType int transportType) {
-        mDataConnection = dc;
-        mTransportType = transportType;
-        logd(dc.getName() + " acquired the ownership of this agent.");
-    }
-
-    /**
-     * Release the ownership of network agent.
-     */
-    public synchronized void releaseOwnership(DataConnection dc) {
-        if (mDataConnection == null) {
-            loge("releaseOwnership called on no-owner DcNetworkAgent!");
-            return;
-        } else if (mDataConnection != dc) {
-            loge("releaseOwnership: This agent belongs to "
-                    + mDataConnection.getName() + ", ignored the request from " + dc.getName());
-            return;
-        }
-        logd("Data connection " + mDataConnection.getName() + " released the ownership.");
-        mDataConnection = null;
-    }
-
-    /**
-     * @return The data connection that owns this agent
-     */
-    public synchronized DataConnection getDataConnection() {
-        return mDataConnection;
-    }
-
-    private static final SlidingWindowEventCounter sNetworkUnwantedCounter =
-            new SlidingWindowEventCounter(NETWORK_UNWANTED_ANOMALY_WINDOW_MS,
-                    NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES);
-
-    @Override
-    public synchronized void onNetworkUnwanted() {
-        mHandler.sendEmptyMessageDelayed(EVENT_UNWANTED_TIMEOUT, TimeUnit.SECONDS.toMillis(30));
-        trackNetworkUnwanted();
-        if (mDataConnection == null) {
-            loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!");
-            return;
-        }
-
-        logd("onNetworkUnwanted called. Now tear down the data connection "
-                + mDataConnection.getName());
-        mDataConnection.tearDownAll(Phone.REASON_RELEASED_BY_CONNECTIVITY_SERVICE,
-                DcTracker.RELEASE_TYPE_DETACH, null);
-    }
-
-    /**
-     * There have been several bugs where a RECONNECT loop kicks off where a DataConnection
-     * connects to the Network, ConnectivityService indicates that the Network is unwanted,
-     * and then the DataConnection reconnects.  By the time we get the bug report it's too late
-     * because there have already been hundreds of RECONNECTS.  This is meant to capture the issue
-     * when it first starts.
-     *
-     * The unwanted counter is configured to only take an anomaly report in extreme cases.
-     * This is to avoid having the anomaly message show up on several devices.
-     *
-     * This is directly related to b/175845538.  But, there have been several other occurrences of
-     * this issue.
-     */
-    private void trackNetworkUnwanted() {
-        if (sNetworkUnwantedCounter.addOccurrence()) {
-            AnomalyReporter.reportAnomaly(
-                    UUID.fromString("3f578b5c-64e9-11eb-ae93-0242ac130002"),
-                    "Network Unwanted called 12 times in 5 minutes.", mPhone.getCarrierId());
-        }
-    }
-
-    @Override
-    public synchronized void onBandwidthUpdateRequested() {
-        if (mDataConnection == null) {
-            loge("onBandwidthUpdateRequested called on no-owner DcNetworkAgent!");
-            return;
-        }
-
-        if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE     // active LCE service
-                && mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-            mPhone.mCi.pullLceData(mDataConnection.obtainMessage(
-                    DataConnection.EVENT_BW_REFRESH_RESPONSE));
-        }
-    }
-
-    @Override
-    public synchronized void onValidationStatus(int status, Uri redirectUri) {
-        if (mDataConnection == null) {
-            loge("onValidationStatus called on no-owner DcNetworkAgent!");
-            return;
-        }
-
-        logd("validation status: " + status + " with redirection URL: " + redirectUri);
-        DcTracker dct = mPhone.getDcTracker(mTransportType);
-        if (dct != null) {
-            Message msg = dct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                    status, mDataConnection.getCid(), redirectUri.toString());
-            msg.sendToTarget();
-        }
-    }
-
-    private synchronized boolean isOwned(DataConnection dc, String reason) {
-        if (mDataConnection == null) {
-            loge(reason + " called on no-owner DcNetworkAgent!");
-            return false;
-        } else if (mDataConnection != dc) {
-            loge(reason + ": This agent belongs to "
-                    + mDataConnection.getName() + ", ignored the request from " + dc.getName());
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Update the legacy sub type (i.e. data network type).
-     *
-     * @param dc The data connection that invokes this method.
-     */
-    public synchronized void updateLegacySubtype(DataConnection dc) {
-        if (!isOwned(dc, "updateLegacySubtype")) return;
-
-        int networkType = getNetworkType();
-        setLegacySubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
-    }
-
-    /**
-     * Set the network capabilities.
-     *
-     * @param networkCapabilities The network capabilities.
-     * @param dc The data connection that invokes this method.
-     */
-    public synchronized void sendNetworkCapabilities(NetworkCapabilities networkCapabilities,
-                                                     DataConnection dc) {
-        if (!isOwned(dc, "sendNetworkCapabilities")) return;
-
-        if (!networkCapabilities.equals(mNetworkCapabilities)) {
-            String logStr = "Changed from " + mNetworkCapabilities + " to "
-                    + networkCapabilities + ", Data RAT="
-                    + mPhone.getServiceState().getRilDataRadioTechnology()
-                    + ", dc=" + mDataConnection.getName();
-            logd(logStr);
-            mNetCapsLocalLog.log(logStr);
-            if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-                // only log metrics for DataConnection with NET_CAPABILITY_INTERNET
-                if (mNetworkCapabilities == null
-                        || networkCapabilities.hasCapability(
-                                NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
-                        != mNetworkCapabilities.hasCapability(
-                                NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)) {
-                    TelephonyMetrics.getInstance().writeNetworkCapabilitiesChangedEvent(
-                            mPhone.getPhoneId(), networkCapabilities);
-                }
-            }
-            mNetworkCapabilities = networkCapabilities;
-        }
-        sendNetworkCapabilities(networkCapabilities);
-    }
-
-    /**
-     * Set the link properties
-     *
-     * @param linkProperties The link properties
-     * @param dc The data connection that invokes this method.
-     */
-    public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties,
-                                                DataConnection dc) {
-        if (!isOwned(dc, "sendLinkProperties")) return;
-
-        sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName());
-        sendLinkProperties(linkProperties);
-    }
-
-    /**
-     * Set the network score.
-     *
-     * @param score The network score.
-     * @param dc The data connection that invokes this method.
-     */
-    public synchronized void sendNetworkScore(int score, DataConnection dc) {
-        if (!isOwned(dc, "sendNetworkScore")) return;
-        sendNetworkScore(score);
-    }
-
-    /**
-     * Unregister the network agent from connectivity service.
-     *
-     * @param dc The data connection that invokes this method.
-     */
-    public synchronized void unregister(DataConnection dc) {
-        if (!isOwned(dc, "unregister")) return;
-
-        mHandler.removeMessages(EVENT_UNWANTED_TIMEOUT);
-        logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed.");
-        sInterfaceNames.remove(mId);
-        super.unregister();
-    }
-
-    @Override
-    public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval,
-            @NonNull KeepalivePacketData packet) {
-        if (mDataConnection == null) {
-            loge("onStartSocketKeepalive called on no-owner DcNetworkAgent!");
-            return;
-        }
-
-        if (packet instanceof NattKeepalivePacketData) {
-            mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_START_REQUEST,
-                    slot, (int) interval.getSeconds(), packet).sendToTarget();
-        } else {
-            sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED);
-        }
-    }
-
-    @Override
-    public synchronized void onStopSocketKeepalive(int slot) {
-        if (mDataConnection == null) {
-            loge("onStopSocketKeepalive called on no-owner DcNetworkAgent!");
-            return;
-        }
-
-        mDataConnection.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slot)
-                .sendToTarget();
-    }
-
-    @Override
-    public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
-        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId,
-              new QosCallbackTracker.IFilter() {
-                  @Override
-                  public boolean matchesLocalAddress(
-                          InetAddress address, int startPort, int endPort) {
-                      return filter.matchesLocalAddress(address, startPort, endPort);
-                  }
-
-                  @Override
-                  public boolean matchesRemoteAddress(
-                          InetAddress address, int startPort, int endPort) {
-                      return filter.matchesRemoteAddress(address, startPort, endPort);
-                  }
-              }));
-    }
-
-    @Override
-    public void onQosCallbackUnregistered(final int qosCallbackId) {
-        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId));
-    }
-
-    void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) {
-        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions));
-    }
-
-    @Override
-    public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
-            @NonNull final QosSessionAttributes attributes) {
-        super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
-    }
-
-    @Override
-    public void notifyQosSessionLost(final int qosCallbackId,
-            final int sessionId, final int qosSessionType) {
-        super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
-    }
-
-    @Override
-    public String toString() {
-        return "DcNetworkAgent-"
-                + mId
-                + " mDataConnection="
-                + ((mDataConnection != null) ? mDataConnection.getName() : null)
-                + " mTransportType="
-                + AccessNetworkConstants.transportTypeToString(mTransportType)
-                + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null)
-                + " mNetworkCapabilities=" + mNetworkCapabilities;
-    }
-
-    /**
-     * Dump the state of transport manager
-     *
-     * @param fd File descriptor
-     * @param printWriter Print writer
-     * @param args Arguments
-     */
-    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
-        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
-        pw.println(toString());
-        pw.increaseIndent();
-        pw.println("Net caps logs:");
-        mNetCapsLocalLog.dump(fd, pw, args);
-        pw.decreaseIndent();
-    }
-
-    /**
-     * Log with debug level
-     *
-     * @param s is string log
-     */
-    private void logd(String s) {
-        Rlog.d(mTag, s);
-    }
-
-    /**
-     * Log with error level
-     *
-     * @param s is string log
-     */
-    private void loge(String s) {
-        Rlog.e(mTag, s);
-    }
-
-    class DcKeepaliveTracker {
-        private class KeepaliveRecord {
-            public int slotId;
-            public int currentStatus;
-
-            KeepaliveRecord(int slotId, int status) {
-                this.slotId = slotId;
-                this.currentStatus = status;
-            }
-        }
-
-        private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray();
-
-        int getHandleForSlot(int slotId) {
-            for (int i = 0; i < mKeepalives.size(); i++) {
-                KeepaliveRecord kr = mKeepalives.valueAt(i);
-                if (kr.slotId == slotId) return mKeepalives.keyAt(i);
-            }
-            return -1;
-        }
-
-        int keepaliveStatusErrorToPacketKeepaliveError(int error) {
-            switch(error) {
-                case KeepaliveStatus.ERROR_NONE:
-                    return SocketKeepalive.SUCCESS;
-                case KeepaliveStatus.ERROR_UNSUPPORTED:
-                    return SocketKeepalive.ERROR_UNSUPPORTED;
-                case KeepaliveStatus.ERROR_NO_RESOURCES:
-                    return SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
-                case KeepaliveStatus.ERROR_UNKNOWN:
-                default:
-                    return SocketKeepalive.ERROR_HARDWARE_ERROR;
-            }
-        }
-
-        void handleKeepaliveStarted(final int slot, KeepaliveStatus ks) {
-            switch (ks.statusCode) {
-                case KeepaliveStatus.STATUS_INACTIVE:
-                    DcNetworkAgent.this.sendSocketKeepaliveEvent(slot,
-                            keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
-                    break;
-                case KeepaliveStatus.STATUS_ACTIVE:
-                    DcNetworkAgent.this.sendSocketKeepaliveEvent(
-                            slot, SocketKeepalive.SUCCESS);
-                    // fall through to add record
-                case KeepaliveStatus.STATUS_PENDING:
-                    logd("Adding keepalive handle="
-                            + ks.sessionHandle + " slot = " + slot);
-                    mKeepalives.put(ks.sessionHandle,
-                            new KeepaliveRecord(
-                                    slot, ks.statusCode));
-                    break;
-                default:
-                    logd("Invalid KeepaliveStatus Code: " + ks.statusCode);
-                    break;
-            }
-        }
-
-        void handleKeepaliveStatus(KeepaliveStatus ks) {
-            final KeepaliveRecord kr;
-            kr = mKeepalives.get(ks.sessionHandle);
-
-            if (kr == null) {
-                // If there is no slot for the session handle, we received an event
-                // for a different data connection. This is not an error because the
-                // keepalive session events are broadcast to all listeners.
-                loge("Discarding keepalive event for different data connection:" + ks);
-                return;
-            }
-            // Switch on the current state, to see what we do with the status update
-            switch (kr.currentStatus) {
-                case KeepaliveStatus.STATUS_INACTIVE:
-                    logd("Inactive Keepalive received status!");
-                    DcNetworkAgent.this.sendSocketKeepaliveEvent(
-                            kr.slotId, SocketKeepalive.ERROR_HARDWARE_ERROR);
-                    break;
-                case KeepaliveStatus.STATUS_PENDING:
-                    switch (ks.statusCode) {
-                        case KeepaliveStatus.STATUS_INACTIVE:
-                            DcNetworkAgent.this.sendSocketKeepaliveEvent(kr.slotId,
-                                    keepaliveStatusErrorToPacketKeepaliveError(ks.errorCode));
-                            kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
-                            mKeepalives.remove(ks.sessionHandle);
-                            break;
-                        case KeepaliveStatus.STATUS_ACTIVE:
-                            logd("Pending Keepalive received active status!");
-                            kr.currentStatus = KeepaliveStatus.STATUS_ACTIVE;
-                            DcNetworkAgent.this.sendSocketKeepaliveEvent(
-                                    kr.slotId, SocketKeepalive.SUCCESS);
-                            break;
-                        case KeepaliveStatus.STATUS_PENDING:
-                            loge("Invalid unsolicied Keepalive Pending Status!");
-                            break;
-                        default:
-                            loge("Invalid Keepalive Status received, " + ks.statusCode);
-                    }
-                    break;
-                case KeepaliveStatus.STATUS_ACTIVE:
-                    switch (ks.statusCode) {
-                        case KeepaliveStatus.STATUS_INACTIVE:
-                            logd("Keepalive received stopped status!");
-                            DcNetworkAgent.this.sendSocketKeepaliveEvent(
-                                    kr.slotId, SocketKeepalive.SUCCESS);
-
-                            kr.currentStatus = KeepaliveStatus.STATUS_INACTIVE;
-                            mKeepalives.remove(ks.sessionHandle);
-                            break;
-                        case KeepaliveStatus.STATUS_PENDING:
-                        case KeepaliveStatus.STATUS_ACTIVE:
-                            loge("Active Keepalive received invalid status!");
-                            break;
-                        default:
-                            loge("Invalid Keepalive Status received, " + ks.statusCode);
-                    }
-                    break;
-                default:
-                    loge("Invalid Keepalive Status received, " + kr.currentStatus);
-            }
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
deleted file mode 100644
index da775e8..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.NetworkRequest;
-import android.net.NetworkSpecifier;
-import android.net.TelephonyNetworkSpecifier;
-import android.telephony.Annotation.ApnType;
-
-import com.android.telephony.Rlog;
-
-/**
- * Wraps cellular network requests to configured apn types.
- */
-public class DcRequest implements Comparable<DcRequest> {
-    private static final String LOG_TAG = "DcRequest";
-
-    @NonNull
-    public final NetworkRequest networkRequest;
-    public final int priority;
-    public final @ApnType int apnType;
-
-    private DcRequest(@NonNull final NetworkRequest nr, @ApnType final int type,
-            int apnPriority) {
-        networkRequest = nr;
-        priority = apnPriority;
-        apnType = type;
-    }
-
-    /**
-     * Create a DcRequest based off of the network request.  If the network request is not cellular,
-     * then null is returned and a warning is generated.
-     * @param networkRequest sets the type of dc request
-     * @param apnConfigTypeRepository apn config types to match on the network request
-     * @return corresponding DcRequest
-     *
-     */
-    @Nullable
-    public static DcRequest create(@NonNull final NetworkRequest networkRequest,
-            @NonNull final ApnConfigTypeRepository apnConfigTypeRepository) {
-        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
-        final ApnConfigType apnConfigType = apnConfigTypeRepository.getByType(apnType);
-        if (apnConfigType == null) {
-            Rlog.d(LOG_TAG, "Non cellular request ignored: " + networkRequest.toString());
-            checkForAnomalousNetworkRequest(networkRequest);
-            return null;
-        } else {
-            Rlog.d(LOG_TAG, "Cellular request confirmed: " + networkRequest.toString());
-            return new DcRequest(networkRequest, apnType, apnConfigType.getPriority());
-        }
-    }
-
-    private static void checkForAnomalousNetworkRequest(NetworkRequest networkRequest) {
-        NetworkSpecifier specifier = networkRequest.getNetworkSpecifier();
-        if (specifier != null) {
-            if (specifier instanceof TelephonyNetworkSpecifier) {
-                reportAnomalousNetworkRequest(networkRequest);
-            }
-        }
-    }
-
-    private static void reportAnomalousNetworkRequest(NetworkRequest networkRequest) {
-        //TODO: Report anomaly if this happens
-        Rlog.w(LOG_TAG, "A TelephonyNetworkSpecifier for a non-cellular request is invalid: "
-                + networkRequest.toString());
-
-    }
-
-    public String toString() {
-        return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType;
-    }
-
-    public int hashCode() {
-        return networkRequest.hashCode();
-    }
-
-    public boolean equals(Object o) {
-        if (o instanceof DcRequest) {
-            return networkRequest.equals(((DcRequest)o).networkRequest);
-        }
-        return false;
-    }
-
-    public int compareTo(DcRequest o) {
-        return o.priority - priority;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
deleted file mode 100644
index 11a0ae6..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-/**
- * To bring down all DC's send the following intent:
- *
- * adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all
- */
-public class DcTesterDeactivateAll {
-    private static final String LOG_TAG = "DcTesterDeacativateAll";
-    private static final boolean DBG = true;
-
-    private Phone mPhone;
-    private DcController mDcc;
-
-    public static String sActionDcTesterDeactivateAll =
-            "com.android.internal.telephony.dataconnection.action_deactivate_all";
-
-
-    // The static intent receiver one for all instances and we assume this
-    // is running on the same thread as Dcc.
-    protected BroadcastReceiver sIntentReceiver = new BroadcastReceiver() {
-            @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) log("sIntentReceiver.onReceive: action=" + action);
-            if (action.equals(sActionDcTesterDeactivateAll)
-                    || action.equals(mPhone.getActionDetached())) {
-                log("Send DEACTIVATE to all Dcc's");
-                if (mDcc != null) {
-                    for (DataConnection dc : mDcc.mDcListAll) {
-                        dc.tearDownNow();
-                    }
-                } else {
-                    if (DBG) log("onReceive: mDcc is null, ignoring");
-                }
-            } else {
-                if (DBG) log("onReceive: unknown action=" + action);
-            }
-        }
-    };
-
-    DcTesterDeactivateAll(Phone phone, DcController dcc, Handler handler) {
-        mPhone = phone;
-        mDcc = dcc;
-
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            IntentFilter filter = new IntentFilter();
-
-            filter.addAction(sActionDcTesterDeactivateAll);
-            log("register for intent action=" + sActionDcTesterDeactivateAll);
-
-            filter.addAction(mPhone.getActionDetached());
-            log("register for intent action=" + mPhone.getActionDetached());
-
-            phone.getContext().registerReceiver(sIntentReceiver, filter, null, handler,
-                    Context.RECEIVER_EXPORTED);
-        }
-    }
-
-    void dispose() {
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            mPhone.getContext().unregisterReceiver(sIntentReceiver);
-        }
-    }
-
-    private static void log(String s) {
-        Rlog.d(LOG_TAG, s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
deleted file mode 100644
index 788da29..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.telephony.DataFailCause;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.telephony.Rlog;
-
-/**
- * A package level call that causes all DataConnection bringUp calls to fail a specific
- * number of times. Here is an example that sets counter to 2 and cause to -3 for all instances:
- *    adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup \
- *     --ei counter 2 --ei fail_cause -3
- *
- * Also you can add a suggested retry time if desired:
- *     --ei suggested_retry_time 5000
- *
- * The fail_cause is one of {@link DataFailCause}
- */
-public class DcTesterFailBringUpAll {
-    private static final String LOG_TAG = "DcTesterFailBrinupAll";
-    private static final boolean DBG = true;
-
-    private Phone mPhone;
-
-    private String mActionFailBringUp = DcFailBringUp.INTENT_BASE + "."
-            + DcFailBringUp.ACTION_FAIL_BRINGUP;
-
-    // The saved FailBringUp data from the intent
-    private DcFailBringUp mFailBringUp = new DcFailBringUp();
-
-    // The static intent receiver one for all instances.
-    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-            @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) log("sIntentReceiver.onReceive: action=" + action);
-            if (action.equals(mActionFailBringUp)) {
-                mFailBringUp.saveParameters(intent, "sFailBringUp");
-            } else if (action.equals(mPhone.getActionDetached())) {
-                // Counter is MAX, bringUp/retry will always fail
-                log("simulate detaching");
-                mFailBringUp.saveParameters(Integer.MAX_VALUE,
-                        DataFailCause.LOST_CONNECTION,
-                        DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME);
-            } else if (action.equals(mPhone.getActionAttached())) {
-                // Counter is 0 next bringUp/retry will succeed
-                log("simulate attaching");
-                mFailBringUp.saveParameters(0, DataFailCause.NONE,
-                        DcFailBringUp.DEFAULT_SUGGESTED_RETRY_TIME);
-            } else {
-                if (DBG) log("onReceive: unknown action=" + action);
-            }
-        }
-    };
-
-    DcTesterFailBringUpAll(Phone phone, Handler handler) {
-        mPhone = phone;
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            IntentFilter filter = new IntentFilter();
-
-            filter.addAction(mActionFailBringUp);
-            log("register for intent action=" + mActionFailBringUp);
-
-            filter.addAction(mPhone.getActionDetached());
-            log("register for intent action=" + mPhone.getActionDetached());
-
-            filter.addAction(mPhone.getActionAttached());
-            log("register for intent action=" + mPhone.getActionAttached());
-
-            phone.getContext().registerReceiver(mIntentReceiver, filter, null, handler,
-                    Context.RECEIVER_EXPORTED);
-        }
-    }
-
-    void dispose() {
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            mPhone.getContext().unregisterReceiver(mIntentReceiver);
-        }
-    }
-
-    public DcFailBringUp getDcFailBringUp() {
-        return mFailBringUp;
-    }
-
-    private void log(String s) {
-        Rlog.d(LOG_TAG, s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
deleted file mode 100755
index 28f69dc..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ /dev/null
@@ -1,5665 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
-import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_DO_FALLBACK;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
-import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL;
-
-import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
-import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkCapabilities;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkRequest;
-import android.net.TrafficStats;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RegistrantList;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.Annotation.NetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.CellLocation;
-import android.telephony.DataFailCause;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.ServiceState.RilRadioTechnology;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyDisplayInfo;
-import android.telephony.TelephonyFrameworkInitializer;
-import android.telephony.TelephonyManager;
-import android.telephony.TelephonyManager.SimState;
-import android.telephony.cdma.CdmaCellLocation;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataCallResponse.HandoverFailureMode;
-import android.telephony.data.DataProfile;
-import android.telephony.data.ThrottleStatus;
-import android.telephony.gsm.GsmCellLocation;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.WindowManager;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.SettingsObserver;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
-import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.PhoneSwitcher;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
-import com.android.internal.telephony.metrics.DataStallRecoveryStats;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.telephony.util.NotificationChannelController;
-import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.internal.util.AsyncChannel;
-import com.android.telephony.Rlog;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-/**
- * {@hide}
- */
-public class DcTracker extends Handler {
-    protected static final boolean DBG = true;
-    private static final boolean VDBG = false; // STOPSHIP if true
-    private static final boolean VDBG_STALL = false; // STOPSHIP if true
-    private static final boolean RADIO_TESTS = false;
-    private static final String NOTIFICATION_TAG = DcTracker.class.getSimpleName();
-
-    @IntDef(value = {
-            REQUEST_TYPE_NORMAL,
-            REQUEST_TYPE_HANDOVER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RequestNetworkType {}
-
-    /**
-     * Normal request for {@link #requestNetwork(NetworkRequest, int, Message)}. For request
-     * network, this adds the request to the {@link ApnContext}. If there were no network request
-     * attached to the {@link ApnContext} earlier, this request setups a data connection.
-     */
-    public static final int REQUEST_TYPE_NORMAL = 1;
-
-    /**
-     * Handover request for {@link #requestNetwork(NetworkRequest, int, Message)} or
-     * {@link #releaseNetwork(NetworkRequest, int)}. For request network, this
-     * initiates the handover data setup process. The existing data connection will be seamlessly
-     * handover to the new network. For release network, this performs a data connection softly
-     * clean up at the underlying layer (versus normal data release).
-     */
-    public static final int REQUEST_TYPE_HANDOVER = 2;
-
-    @IntDef(value = {
-            RELEASE_TYPE_NORMAL,
-            RELEASE_TYPE_DETACH,
-            RELEASE_TYPE_HANDOVER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ReleaseNetworkType {}
-
-    /**
-     * For release network, this is just removing the network request from the {@link ApnContext}.
-     * Note this does not tear down the physical data connection. Normally the data connection is
-     * torn down by connectivity service directly calling {@link NetworkAgent#unwanted()}.
-     */
-    public static final int RELEASE_TYPE_NORMAL = 1;
-
-    /**
-     * Detach request for {@link #releaseNetwork(NetworkRequest, int)} only. This
-     * forces the APN context detach from the data connection. If this {@link ApnContext} is the
-     * last one attached to the data connection, the data connection will be torn down, otherwise
-     * the data connection remains active.
-     */
-    public static final int RELEASE_TYPE_DETACH = 2;
-
-    /**
-     * Handover request for {@link #releaseNetwork(NetworkRequest, int)}. For release
-     * network, this performs a data connection softly clean up at the underlying layer (versus
-     * normal data release).
-     */
-    public static final int RELEASE_TYPE_HANDOVER = 3;
-
-    /** The extras for handover completion message */
-    public static final String DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST = "extra_network_request";
-    public static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type";
-    public static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success";
-    /**
-     * The flag indicates whether after handover failure, the data connection should remain on the
-     * original transport.
-     */
-    public static final String DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK =
-            "extra_handover_failure_fallback";
-
-    private final String mLogTag;
-
-    public AtomicBoolean isCleanupRequired = new AtomicBoolean(false);
-
-    private final TelephonyManager mTelephonyManager;
-
-    private final AlarmManager mAlarmManager;
-
-    /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
-    private int mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
-    // All data enabling/disabling related settings
-    private final DataEnabledSettings mDataEnabledSettings;
-
-    /**
-     * After detecting a potential connection problem, this is the max number
-     * of subsequent polls before attempting recovery.
-     */
-    // 1 sec. default polling interval when screen is on.
-    private static final int POLL_NETSTAT_MILLIS = 1000;
-    // 10 min. default polling interval when screen is off.
-    private static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
-    // Default sent packets without ack which triggers initial recovery steps
-    private static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
-
-    // Default for the data stall alarm while non-aggressive stall detection
-    private static final int DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60 * 6;
-    // Default for the data stall alarm for aggressive stall detection
-    private static final int DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT = 1000 * 60;
-
-    private static final boolean DATA_STALL_SUSPECTED = true;
-    protected static final boolean DATA_STALL_NOT_SUSPECTED = false;
-
-    private static final String INTENT_DATA_STALL_ALARM =
-            "com.android.internal.telephony.data-stall";
-    // Tag for tracking stale alarms
-    private static final String INTENT_DATA_STALL_ALARM_EXTRA_TAG = "data_stall_alarm_extra_tag";
-    private static final String INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE =
-            "data_stall_alarm_extra_transport_type";
-
-    // Unique id for no data notification on setup data permanently failed.
-    private static final int NO_DATA_NOTIFICATION = 1001;
-
-    /** The higher index has higher priority. */
-    private static final DctConstants.State[] DATA_CONNECTION_STATE_PRIORITIES = {
-            DctConstants.State.IDLE,
-            DctConstants.State.DISCONNECTING,
-            DctConstants.State.CONNECTING,
-            DctConstants.State.CONNECTED,
-    };
-
-    private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
-    private DcController mDcc;
-
-    /** kept in sync with mApnContexts
-     * Higher numbers are higher priority and sorted so highest priority is first */
-    private ArrayList<ApnContext> mPrioritySortedApnContexts = new ArrayList<>();
-
-    /** all APN settings applicable to the current carrier */
-    private ArrayList<ApnSetting> mAllApnSettings = new ArrayList<>();
-
-    /** preferred apn */
-    private ApnSetting mPreferredApn = null;
-
-    /** Is packet service restricted by network */
-    private boolean mIsPsRestricted = false;
-
-    /** emergency apn Setting*/
-    private ApnSetting mEmergencyApn = null;
-
-    /* Once disposed dont handle any messages */
-    private boolean mIsDisposed = false;
-
-    private ContentResolver mResolver;
-
-    /* Set to true with CMD_ENABLE_MOBILE_PROVISIONING */
-    private boolean mIsProvisioning = false;
-
-    /* The Url passed as object parameter in CMD_ENABLE_MOBILE_PROVISIONING */
-    private String mProvisioningUrl = null;
-
-    /* Indicating data service is bound or not */
-    private boolean mDataServiceBound = false;
-
-    /* Intent to hide/show the sign-in error notification for provisioning */
-    private static final String INTENT_PROVISION = "com.android.internal.telephony.PROVISION";
-
-    /**
-     * Extra containing the phone ID for INTENT_PROVISION
-     * Must be kept consistent with NetworkNotificationManager#setProvNotificationVisible.
-     * TODO: refactor the deprecated API to prevent hardcoding values.
-     */
-    private static final String EXTRA_PROVISION_PHONE_ID = "provision.phone.id";
-
-    /* Intent for the provisioning apn alarm */
-    private static final String INTENT_PROVISIONING_APN_ALARM =
-            "com.android.internal.telephony.provisioning_apn_alarm";
-
-    /* Tag for tracking stale alarms */
-    private static final String PROVISIONING_APN_ALARM_TAG_EXTRA = "provisioning.apn.alarm.tag";
-
-    /* Debug property for overriding the PROVISIONING_APN_ALARM_DELAY_IN_MS */
-    private static final String DEBUG_PROV_APN_ALARM = "persist.debug.prov_apn_alarm";
-
-    /* Default for the provisioning apn alarm timeout */
-    private static final int PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 15;
-
-    /* The provision apn alarm intent used to disable the provisioning apn */
-    private PendingIntent mProvisioningApnAlarmIntent = null;
-
-    /* Used to track stale provisioning apn alarms */
-    private int mProvisioningApnAlarmTag = (int) SystemClock.elapsedRealtime();
-
-    private AsyncChannel mReplyAc = new AsyncChannel();
-
-    private final LocalLog mDataRoamingLeakageLog = new LocalLog(32);
-    private final LocalLog mApnSettingsInitializationLog = new LocalLog(32);
-
-    /* 5G connection reevaluation watchdog alarm constants */
-    private long mWatchdogTimeMs = 1000 * 60 * 60;
-    private boolean mWatchdog = false;
-
-    /* Default for whether 5G frequencies are considered unmetered */
-    private boolean mNrNsaAllUnmetered = false;
-    private boolean mNrNsaMmwaveUnmetered = false;
-    private boolean mNrNsaSub6Unmetered = false;
-    private boolean mNrSaAllUnmetered = false;
-    private boolean mNrSaMmwaveUnmetered = false;
-    private boolean mNrSaSub6Unmetered = false;
-    private boolean mNrNsaRoamingUnmetered = false;
-
-    // it effect the PhysicalLinkStatusChanged
-    private boolean mLteEndcUsingUserDataForRrcDetection = false;
-
-    /* List of SubscriptionPlans, updated when initialized and when plans are changed. */
-    private List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
-    /* List of network types an unmetered override applies to, set by onSubscriptionOverride
-     * and cleared when the device is rebooted or the override expires. */
-    private List<Integer> mUnmeteredNetworkTypes = null;
-    /* List of network types a congested override applies to, set by onSubscriptionOverride
-     * and cleared when the device is rebooted or the override expires. */
-    private List<Integer> mCongestedNetworkTypes = null;
-    /* Whether an unmetered override is currently active. */
-    private boolean mUnmeteredOverride = false;
-    /* Whether a congested override is currently active. */
-    private boolean mCongestedOverride = false;
-
-    @SimState
-    private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
-
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-
-            if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                // TODO: Evaluate hooking this up with DeviceStateMonitor
-                if (DBG) log("screen on");
-                mIsScreenOn = true;
-                stopNetStatPoll();
-                startNetStatPoll();
-                restartDataStallAlarm();
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                if (DBG) log("screen off");
-                mIsScreenOn = false;
-                stopNetStatPoll();
-                startNetStatPoll();
-                restartDataStallAlarm();
-            } else if (action.equals(INTENT_DATA_STALL_ALARM)) {
-                onActionIntentDataStallAlarm(intent);
-            } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) {
-                if (DBG) log("Provisioning apn alarm");
-                onActionIntentProvisioningApnAlarm(intent);
-            } else if (action.equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
-                    || action.equals(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)) {
-                if (mPhone.getPhoneId() == intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
-                    int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
-                            TelephonyManager.SIM_STATE_UNKNOWN);
-                    sendMessage(obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED, simState, 0));
-                }
-            } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
-                    if (intent.getBooleanExtra(
-                            CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
-                        // Ignore the rebroadcast one to prevent multiple carrier config changed
-                        // event during boot up.
-                        return;
-                    }
-                    int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
-                            SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                    if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                        sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
-                    }
-                }
-            } else {
-                if (DBG) log("onReceive: Unknown action=" + action);
-            }
-        }
-    };
-
-    private final Runnable mPollNetStat = new Runnable() {
-        @Override
-        public void run() {
-            updateDataActivity();
-
-            if (mIsScreenOn) {
-                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
-                        Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
-            } else {
-                mNetStatPollPeriod = Settings.Global.getInt(mResolver,
-                        Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
-                        POLL_NETSTAT_SCREEN_OFF_MILLIS);
-            }
-
-            if (mNetStatPollEnabled) {
-                mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
-            }
-        }
-    };
-
-    private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
-        @Override
-        public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
-            for (ThrottleStatus status : throttleStatuses) {
-                if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) {
-                    setupDataOnConnectableApn(mApnContextsByType.get(status.getApnType()),
-                            Phone.REASON_DATA_UNTHROTTLED,
-                            RetryFailures.ALWAYS);
-                }
-            }
-        }
-    }
-
-    private NetworkPolicyManager mNetworkPolicyManager;
-    private final NetworkPolicyManager.SubscriptionCallback mSubscriptionCallback =
-            new NetworkPolicyManager.SubscriptionCallback() {
-        @Override
-        public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-                int[] networkTypes) {
-            if (mPhone == null || mPhone.getSubId() != subId) return;
-
-            List<Integer> tempList = new ArrayList<>();
-            for (int networkType : networkTypes) {
-                tempList.add(networkType);
-            }
-
-            log("Subscription override: overrideMask=" + overrideMask
-                    + ", overrideValue=" + overrideValue + ", networkTypes=" + tempList);
-
-            if (overrideMask == SUBSCRIPTION_OVERRIDE_UNMETERED) {
-                mUnmeteredNetworkTypes = tempList;
-                mUnmeteredOverride = overrideValue != 0;
-                reevaluateUnmeteredConnections();
-            } else if (overrideMask == SUBSCRIPTION_OVERRIDE_CONGESTED) {
-                mCongestedNetworkTypes = tempList;
-                mCongestedOverride = overrideValue != 0;
-                reevaluateCongestedConnections();
-            }
-        }
-
-        @Override
-        public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) {
-            if (mPhone == null || mPhone.getSubId() != subId) return;
-
-            mSubscriptionPlans = Arrays.asList(plans);
-            if (DBG) log("SubscriptionPlans changed: " + mSubscriptionPlans);
-            reevaluateUnmeteredConnections();
-        }
-    };
-
-    private final SettingsObserver mSettingsObserver;
-
-    private void registerSettingsObserver() {
-        mSettingsObserver.unobserve();
-        String simSuffix = "";
-        if (TelephonyManager.getDefault().getSimCount() > 1) {
-            simSuffix = Integer.toString(mPhone.getSubId());
-        }
-
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix),
-                DctConstants.EVENT_ROAMING_SETTING_CHANGE);
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
-    }
-
-    /**
-     * Maintain the sum of transmit and receive packets.
-     *
-     * The packet counts are initialized and reset to -1 and
-     * remain -1 until they can be updated.
-     */
-    public static class TxRxSum {
-        public long txPkts;
-        public long rxPkts;
-
-        public TxRxSum() {
-            reset();
-        }
-
-        public TxRxSum(long txPkts, long rxPkts) {
-            this.txPkts = txPkts;
-            this.rxPkts = rxPkts;
-        }
-
-        public TxRxSum(TxRxSum sum) {
-            txPkts = sum.txPkts;
-            rxPkts = sum.rxPkts;
-        }
-
-        public void reset() {
-            txPkts = -1;
-            rxPkts = -1;
-        }
-
-        @Override
-        public String toString() {
-            return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
-        }
-
-        /**
-         * Get total Tx/Rx packet count from TrafficStats
-         */
-        public void updateTotalTxRxSum() {
-            this.txPkts = TrafficStats.getMobileTxPackets();
-            this.rxPkts = TrafficStats.getMobileRxPackets();
-        }
-    }
-
-    private void onDataReconnect(ApnContext apnContextforRetry, int subId,
-            @RequestNetworkType int requestType) {
-        int phoneSubId = mPhone.getSubId();
-        String apnType = apnContextforRetry.getApnType();
-        String reason =  apnContextforRetry.getReason();
-
-        if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != phoneSubId)) {
-            log("onDataReconnect: invalid subId");
-            return;
-        }
-
-        ApnContext apnContext = mApnContexts.get(apnType);
-
-        if (DBG) {
-            log("onDataReconnect: mState=" + mState + " reason=" + reason + " apnType=" + apnType
-                    + " apnContext=" + apnContext);
-        }
-
-        if ((apnContext != null) && (apnContext.isEnabled())) {
-            apnContext.setReason(reason);
-            DctConstants.State apnContextState = apnContext.getState();
-            if (DBG) {
-                log("onDataReconnect: apnContext state=" + apnContextState);
-            }
-            if ((apnContextState == DctConstants.State.FAILED)
-                    || (apnContextState == DctConstants.State.IDLE)) {
-                if (DBG) {
-                    log("onDataReconnect: state is FAILED|IDLE, disassociate");
-                }
-                apnContext.releaseDataConnection("");
-            } else {
-                if (DBG) log("onDataReconnect: keep associated");
-            }
-            // TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
-            sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, requestType,
-                    0, apnContext));
-        }
-    }
-
-    private void onActionIntentDataStallAlarm(Intent intent) {
-        if (VDBG_STALL) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
-
-        int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        if (!SubscriptionManager.isValidSubscriptionId(subId) || (subId != mPhone.getSubId())) {
-            return;
-        }
-
-        int transportType = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, 0);
-        if (transportType != mTransportType) {
-            return;
-        }
-
-        Message msg = obtainMessage(DctConstants.EVENT_DATA_STALL_ALARM,
-                intent.getAction());
-        msg.arg1 = intent.getIntExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, 0);
-        sendMessage(msg);
-    }
-
-    private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
-
-    // member variables
-    protected final Phone mPhone;
-    private DctConstants.Activity mActivity = DctConstants.Activity.NONE;
-    private DctConstants.State mState = DctConstants.State.IDLE;
-    private final Handler mDataConnectionTracker;
-
-    private long mTxPkts;
-    private long mRxPkts;
-    private int mNetStatPollPeriod;
-    private boolean mNetStatPollEnabled = false;
-
-    private TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
-    // Used to track stale data stall alarms.
-    private int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
-    // The current data stall alarm intent
-    private PendingIntent mDataStallAlarmIntent = null;
-    // Number of packets sent since the last received packet
-    private long mSentSinceLastRecv;
-    // Controls when a simple recovery attempt it to be tried
-    private int mNoRecvPollCount = 0;
-    // Reference counter for enabling fail fast
-    private static int sEnableFailFastRefCounter = 0;
-    // True if data stall detection is enabled
-    private volatile boolean mDataStallNoRxEnabled = true;
-
-    protected volatile boolean mFailFast = false;
-
-    // True when in voice call
-    protected boolean mInVoiceCall = false;
-
-    /** Intent sent when the reconnect alarm fires. */
-    private PendingIntent mReconnectIntent = null;
-
-    // When false we will not auto attach and manually attaching is required.
-    protected boolean mAutoAttachOnCreationConfig = false;
-    private AtomicBoolean mAutoAttachEnabled = new AtomicBoolean(false);
-
-    // State of screen
-    // (TODO: Reconsider tying directly to screen, maybe this is
-    //        really a lower power mode")
-    private boolean mIsScreenOn = true;
-
-    /** Allows the generation of unique Id's for DataConnection objects */
-    private AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
-
-    /** The data connections. */
-    private HashMap<Integer, DataConnection> mDataConnections =
-            new HashMap<Integer, DataConnection>();
-
-    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
-    private HashMap<String, Integer> mApnToDataConnectionId = new HashMap<String, Integer>();
-
-    /** Phone.APN_TYPE_* ===> ApnContext */
-    protected ConcurrentHashMap<String, ApnContext> mApnContexts =
-            new ConcurrentHashMap<String, ApnContext>();
-
-    private SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
-
-    private ArrayList<DataProfile> mLastDataProfileList = new ArrayList<>();
-
-    /** RAT name ===> (downstream, upstream) bandwidth values from carrier config. */
-    private ConcurrentHashMap<String, Pair<Integer, Integer>> mBandwidths =
-            new ConcurrentHashMap<>();
-
-    private boolean mConfigReady = false;
-
-    /**
-     * Handles changes to the APN db.
-     */
-    private class ApnChangeObserver extends ContentObserver {
-        public ApnChangeObserver () {
-            super(mDataConnectionTracker);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
-        }
-    }
-
-    //***** Instance Variables
-
-    private boolean mReregisterOnReconnectFailure = false;
-
-
-    //***** Constants
-
-    private static final int PROVISIONING_SPINNER_TIMEOUT_MILLIS = 120 * 1000;
-
-    static final Uri PREFERAPN_NO_UPDATE_URI_USING_SUBID =
-                        Uri.parse("content://telephony/carriers/preferapn_no_update/subId/");
-    static final String APN_ID = "apn_id";
-
-    private boolean mCanSetPreferApn = false;
-
-    private AtomicBoolean mAttached = new AtomicBoolean(false);
-
-    /** Watches for changes to the APN db. */
-    private ApnChangeObserver mApnObserver;
-
-    private BroadcastReceiver mProvisionBroadcastReceiver;
-    private ProgressDialog mProvisioningSpinner;
-
-    private final DataServiceManager mDataServiceManager;
-
-    @AccessNetworkConstants.TransportType
-    private final int mTransportType;
-
-    private DataStallRecoveryHandler mDsRecoveryHandler;
-    private HandlerThread mHandlerThread;
-
-    private final DataThrottler mDataThrottler;
-
-    private final ThrottleStatusChangedCallback mThrottleStatusCallback;
-
-    /**
-     * Request network completion message map. Key is the APN type, value is the list of completion
-     * messages to be sent. Using a list because there might be multiple network requests for
-     * the same APN type.
-     */
-    private final Map<Integer, List<Message>> mHandoverCompletionMsgs = new HashMap<>();
-
-    //***** Constructor
-    public DcTracker(Phone phone, @TransportType int transportType) {
-        super();
-        mPhone = phone;
-        if (DBG) log("DCT.constructor");
-        mTelephonyManager = TelephonyManager.from(phone.getContext())
-                .createForSubscriptionId(phone.getSubId());
-        // The 'C' in tag indicates cellular, and 'I' indicates IWLAN. This is to distinguish
-        // between two DcTrackers, one for each.
-        String tagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                ? "C" : "I");
-        tagSuffix += "-" + mPhone.getPhoneId();
-        mLogTag = "DCT" + tagSuffix;
-
-        mTransportType = transportType;
-        mDataServiceManager = new DataServiceManager(phone, transportType, tagSuffix);
-        mDataThrottler = new DataThrottler(mPhone, transportType);
-
-        mResolver = mPhone.getContext().getContentResolver();
-        mAlarmManager =
-                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-
-        mDsRecoveryHandler = new DataStallRecoveryHandler();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(INTENT_DATA_STALL_ALARM);
-        filter.addAction(INTENT_PROVISIONING_APN_ALARM);
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
-        filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
-
-        mDataEnabledSettings = mPhone.getDataEnabledSettings();
-
-        mDataEnabledSettings.registerForDataEnabledChanged(this,
-                DctConstants.EVENT_DATA_ENABLED_CHANGED, null);
-        mDataEnabledSettings.registerForDataEnabledOverrideChanged(this,
-                DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED);
-
-        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
-
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        mAutoAttachEnabled.set(sp.getBoolean(Phone.DATA_DISABLED_ON_BOOT_KEY, false));
-
-        mNetworkPolicyManager = (NetworkPolicyManager) mPhone.getContext()
-                .getSystemService(Context.NETWORK_POLICY_SERVICE);
-        mNetworkPolicyManager.registerSubscriptionCallback(mSubscriptionCallback);
-
-        mHandlerThread = new HandlerThread("DcHandlerThread");
-        mHandlerThread.start();
-        Handler dcHandler = new Handler(mHandlerThread.getLooper());
-        mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler.getLooper(),
-                tagSuffix);
-        mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
-
-        mDataConnectionTracker = this;
-        registerForAllEvents();
-        mApnObserver = new ApnChangeObserver();
-        phone.getContext().getContentResolver().registerContentObserver(
-                Telephony.Carriers.CONTENT_URI, true, mApnObserver);
-
-        initApnContexts();
-
-        addDefaultApnSettingsAsNeeded();
-
-        mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
-        registerSettingsObserver();
-
-        mThrottleStatusCallback = new ThrottleStatusChangedCallback();
-        mDataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
-    }
-
-    @VisibleForTesting
-    public DcTracker() {
-        mLogTag = "DCT";
-        mTelephonyManager = null;
-        mAlarmManager = null;
-        mPhone = null;
-        mDataConnectionTracker = null;
-        mSettingsObserver = new SettingsObserver(null, this);
-        mDataEnabledSettings = null;
-        mTransportType = 0;
-        mDataServiceManager = null;
-        mDataThrottler = null;
-        mThrottleStatusCallback = null;
-    }
-
-    public void registerServiceStateTrackerEvents() {
-        mPhone.getServiceStateTracker().registerForDataConnectionAttached(mTransportType, this,
-                DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
-        mPhone.getServiceStateTracker().registerForDataConnectionDetached(mTransportType, this,
-                DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
-        mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
-                DctConstants.EVENT_ROAMING_ON, null);
-        mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
-                DctConstants.EVENT_ROAMING_OFF, null, true);
-        mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
-                DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
-        mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
-                DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
-        mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(mTransportType, this,
-                DctConstants.EVENT_DATA_RAT_CHANGED, null);
-    }
-
-    public void unregisterServiceStateTrackerEvents() {
-        mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(mTransportType, this);
-        mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(mTransportType, this);
-        mPhone.getServiceStateTracker().unregisterForDataRoamingOn(this);
-        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
-        mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
-        mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
-        mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(mTransportType, this);
-        mPhone.getServiceStateTracker().unregisterForAirplaneModeChanged(this);
-    }
-
-    private void registerForAllEvents() {
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-            mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
-            mPhone.mCi.registerForOffOrNotAvailable(this,
-                    DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-            mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
-        }
-
-        // Note, this is fragile - the Phone is now presenting a merged picture
-        // of PS (volte) & CS and by diving into its internals you're just seeing
-        // the CS data.  This works well for the purposes this is currently used for
-        // but that may not always be the case.  Should probably be redesigned to
-        // accurately reflect what we're really interested in (registerForCSVoiceCallEnded).
-        mPhone.getCallTracker().registerForVoiceCallEnded(this,
-                DctConstants.EVENT_VOICE_CALL_ENDED, null);
-        mPhone.getCallTracker().registerForVoiceCallStarted(this,
-                DctConstants.EVENT_VOICE_CALL_STARTED, null);
-        mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(this,
-                DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED, null);
-        registerServiceStateTrackerEvents();
-        mDataServiceManager.registerForServiceBindingChanged(this,
-                DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
-        mDataServiceManager.registerForApnUnthrottled(this, DctConstants.EVENT_APN_UNTHROTTLED);
-    }
-
-    public void dispose() {
-        if (DBG) log("DCT.dispose");
-
-        if (mProvisionBroadcastReceiver != null) {
-            mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
-            mProvisionBroadcastReceiver = null;
-        }
-        if (mProvisioningSpinner != null) {
-            mProvisioningSpinner.dismiss();
-            mProvisioningSpinner = null;
-        }
-
-        cleanUpAllConnectionsInternal(true, null);
-
-        mIsDisposed = true;
-        mPhone.getContext().unregisterReceiver(mIntentReceiver);
-        mSettingsObserver.unobserve();
-
-        mNetworkPolicyManager.unregisterSubscriptionCallback(mSubscriptionCallback);
-        mDcTesterFailBringUpAll.dispose();
-
-        mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
-        mApnContexts.clear();
-        mApnContextsByType.clear();
-        mPrioritySortedApnContexts.clear();
-        unregisterForAllEvents();
-
-        destroyDataConnections();
-    }
-
-    /**
-     * Stop the internal handler thread
-     *
-     * TESTING ONLY
-     */
-    @VisibleForTesting
-    public void stopHandlerThread() {
-        mHandlerThread.quit();
-    }
-
-    private void unregisterForAllEvents() {
-         //Unregister for all events
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-            mPhone.mCi.unregisterForAvailable(this);
-            mPhone.mCi.unregisterForOffOrNotAvailable(this);
-            mPhone.mCi.unregisterForPcoData(this);
-        }
-
-        mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
-        mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
-        mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this);
-        unregisterServiceStateTrackerEvents();
-        mDataServiceManager.unregisterForServiceBindingChanged(this);
-        mDataEnabledSettings.unregisterForDataEnabledChanged(this);
-        mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this);
-        mDataServiceManager.unregisterForApnUnthrottled(this);
-    }
-
-    /**
-     * Reevaluate existing data connections when conditions change.
-     *
-     * For example, handle reverting restricted networks back to unrestricted. If we're changing
-     * user data to enabled and this makes data truly enabled (not disabled by other factors) we
-     * need to reevaluate and possibly add NET_CAPABILITY_NOT_RESTRICTED capability to the data
-     * connection. This allows non-privilege apps to use the network.
-     *
-     * Or when we brought up a unmetered data connection while data is off, we only limit this
-     * data connection for unmetered use only. When data is turned back on, we need to tear that
-     * down so a full capable data connection can be re-established.
-     */
-    private void reevaluateDataConnections() {
-        for (DataConnection dataConnection : mDataConnections.values()) {
-            dataConnection.reevaluateRestrictedState();
-        }
-    }
-
-    public long getSubId() {
-        return mPhone.getSubId();
-    }
-
-    public DctConstants.Activity getActivity() {
-        return mActivity;
-    }
-
-    private void setActivity(DctConstants.Activity activity) {
-        log("setActivity = " + activity);
-        mActivity = activity;
-        mPhone.notifyDataActivity();
-    }
-
-    /**
-     * Request a network
-     *
-     * @param networkRequest Network request from clients
-     * @param type The request type
-     * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
-     * handover is completed. For normal request, this should be null.
-     */
-    public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
-            Message onHandoverCompleteMsg) {
-        if (type != REQUEST_TYPE_HANDOVER && onHandoverCompleteMsg != null) {
-            throw new RuntimeException("request network with normal type request type but passing "
-                    + "handover complete message.");
-        }
-        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
-        final ApnContext apnContext = mApnContextsByType.get(apnType);
-        if (apnContext != null) {
-            apnContext.requestNetwork(networkRequest, type, onHandoverCompleteMsg);
-        }
-    }
-
-    public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
-        final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
-        final ApnContext apnContext = mApnContextsByType.get(apnType);
-        if (apnContext != null) {
-            apnContext.releaseNetwork(networkRequest, type);
-        }
-    }
-
-    // Turn telephony radio on or off.
-    private void setRadio(boolean on) {
-        final ITelephony phone = ITelephony.Stub.asInterface(
-                TelephonyFrameworkInitializer
-                        .getTelephonyServiceManager()
-                        .getTelephonyServiceRegisterer()
-                        .get());
-        try {
-            phone.setRadio(on);
-        } catch (Exception e) {
-            // Ignore.
-        }
-    }
-
-    // Class to handle Intent dispatched with user selects the "Sign-in to network"
-    // notification.
-    private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver {
-        private final String mNetworkOperator;
-        // Mobile provisioning URL.  Valid while provisioning notification is up.
-        // Set prior to notification being posted as URL contains ICCID which
-        // disappears when radio is off (which is the case when notification is up).
-        private final String mProvisionUrl;
-
-        public ProvisionNotificationBroadcastReceiver(String provisionUrl, String networkOperator) {
-            mNetworkOperator = networkOperator;
-            mProvisionUrl = provisionUrl;
-        }
-
-        private void setEnableFailFastMobileData(int enabled) {
-            sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled, 0));
-        }
-
-        private void enableMobileProvisioning() {
-            final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
-            Bundle bundle = new Bundle(1);
-            bundle.putString(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl);
-            msg.setData(bundle);
-            sendMessage(msg);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PROVISION_PHONE_ID,
-                    SubscriptionManager.INVALID_PHONE_INDEX)) {
-                return;
-            }
-            // Turning back on the radio can take time on the order of a minute, so show user a
-            // spinner so they know something is going on.
-            log("onReceive : ProvisionNotificationBroadcastReceiver");
-            mProvisioningSpinner = new ProgressDialog(context);
-            mProvisioningSpinner.setTitle(mNetworkOperator);
-            mProvisioningSpinner.setMessage(
-                    // TODO: Don't borrow "Connecting..." i18n string; give Telephony a version.
-                    context.getText(com.android.internal.R.string.media_route_status_connecting));
-            mProvisioningSpinner.setIndeterminate(true);
-            mProvisioningSpinner.setCancelable(true);
-            // Allow non-Activity Service Context to create a View.
-            mProvisioningSpinner.getWindow().setType(
-                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-            mProvisioningSpinner.show();
-            // After timeout, hide spinner so user can at least use their device.
-            // TODO: Indicate to user that it is taking an unusually long time to connect?
-            sendMessageDelayed(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
-                    mProvisioningSpinner), PROVISIONING_SPINNER_TIMEOUT_MILLIS);
-            // This code is almost identical to the old
-            // ConnectivityService.handleMobileProvisioningAction code.
-            setRadio(true);
-            setEnableFailFastMobileData(DctConstants.ENABLED);
-            enableMobileProvisioning();
-        }
-    }
-
-    @Override
-    protected void finalize() {
-        if(DBG && mPhone != null) log("finalize");
-    }
-
-    private void initApnContexts() {
-        PersistableBundle carrierConfig;
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());
-        } else {
-            carrierConfig = null;
-        }
-        initApnContexts(carrierConfig);
-    }
-
-    //Blows away any existing apncontexts that may exist, only use in ctor.
-    private void initApnContexts(PersistableBundle carrierConfig) {
-        if (!mTelephonyManager.isDataCapable()) {
-            log("initApnContexts: isDataCapable == false.  No Apn Contexts loaded");
-            return;
-        }
-
-        log("initApnContexts: E");
-        // Load device network attributes from resources
-        final Collection<ApnConfigType> types =
-                new ApnConfigTypeRepository(carrierConfig).getTypes();
-
-        for (ApnConfigType apnConfigType : types) {
-            ApnContext apnContext = new ApnContext(mPhone, apnConfigType.getType(), mLogTag, this,
-                    apnConfigType.getPriority());
-            int bitmask = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
-            mPrioritySortedApnContexts.add(apnContext);
-            mApnContexts.put(apnContext.getApnType(), apnContext);
-            mApnContextsByType.put(bitmask, apnContext);
-            // Notify listeners that all data is disconnected when DCT is initialized.
-            // Once connections are established, DC will then notify that data is connected.
-            // This is to prevent the case where the phone process crashed but we don't notify
-            // listeners that data was disconnected, so they may be stuck in a connected state.
-            mPhone.notifyDataConnection(new PreciseDataConnectionState.Builder()
-                    .setTransportType(mTransportType)
-                    .setState(TelephonyManager.DATA_DISCONNECTED)
-                    .setApnSetting(new ApnSetting.Builder()
-                            .setApnTypeBitmask(bitmask).buildWithoutCheck())
-                    .setNetworkType(getDataRat())
-                    .build());
-            log("initApnContexts: apnContext=" + ApnSetting.getApnTypeString(
-                    apnConfigType.getType()));
-        }
-        mPrioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority());
-        logSortedApnContexts();
-    }
-
-    private void sortApnContextByPriority() {
-        if (!mTelephonyManager.isDataCapable()) {
-            log("sortApnContextByPriority: isDataCapable == false.  No Apn Contexts loaded");
-            return;
-        }
-
-        PersistableBundle carrierConfig;
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());
-        } else {
-            carrierConfig = null;
-        }
-
-        log("sortApnContextByPriority: E");
-        // Load device network attributes from resources
-        final Collection<ApnConfigType> types =
-                new ApnConfigTypeRepository(carrierConfig).getTypes();
-        for (ApnConfigType apnConfigType : types) {
-            if (mApnContextsByType.contains(apnConfigType.getType())) {
-                ApnContext apnContext = mApnContextsByType.get(apnConfigType.getType());
-                apnContext.setPriority(apnConfigType.getPriority());
-            }
-        }
-
-        //Doing sorted in a different list to keep thread safety
-        ArrayList<ApnContext> prioritySortedApnContexts =
-                new ArrayList<>(mPrioritySortedApnContexts);
-        prioritySortedApnContexts.sort((c1, c2) -> c2.getPriority() - c1.getPriority());
-        mPrioritySortedApnContexts = prioritySortedApnContexts;
-        logSortedApnContexts();
-    }
-
-    public LinkProperties getLinkProperties(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext != null) {
-            DataConnection dataConnection = apnContext.getDataConnection();
-            if (dataConnection != null) {
-                if (DBG) log("return link properties for " + apnType);
-                return dataConnection.getLinkProperties();
-            }
-        }
-        if (DBG) log("return new LinkProperties");
-        return new LinkProperties();
-    }
-
-    public NetworkCapabilities getNetworkCapabilities(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext!=null) {
-            DataConnection dataConnection = apnContext.getDataConnection();
-            if (dataConnection != null) {
-                if (DBG) {
-                    log("get active pdp is not null, return NetworkCapabilities for " + apnType);
-                }
-                return dataConnection.getNetworkCapabilities();
-            }
-        }
-        if (DBG) log("return new NetworkCapabilities");
-        return new NetworkCapabilities();
-    }
-
-    // Return all active apn types
-    public String[] getActiveApnTypes() {
-        if (DBG) log("get all active apn types");
-        ArrayList<String> result = new ArrayList<String>();
-
-        for (ApnContext apnContext : mApnContexts.values()) {
-            if (mAttached.get() && apnContext.isReady()) {
-                result.add(apnContext.getApnType());
-            }
-        }
-
-        return result.toArray(new String[0]);
-    }
-
-    /**
-     * Get ApnTypes with connected data connections.  This is different than getActiveApnTypes()
-     * which returns apn types that with active apn contexts.
-     * @return apn types
-     */
-    public String[] getConnectedApnTypes() {
-        return mApnContexts.values().stream()
-                .filter(ac -> ac.getState() == DctConstants.State.CONNECTED)
-                .map(ApnContext::getApnType)
-                .toArray(String[]::new);
-    }
-
-    @VisibleForTesting
-    public Collection<ApnContext> getApnContexts() {
-        return mPrioritySortedApnContexts;
-    }
-
-    /** Return active ApnSetting of a specific apnType */
-    public ApnSetting getActiveApnSetting(String apnType) {
-        if (VDBG) log("get active ApnSetting for type:" + apnType);
-        ApnContext apnContext = mApnContexts.get(apnType);
-        return (apnContext != null) ? apnContext.getApnSetting() : null;
-    }
-
-    // Return active apn of specific apn type
-    public String getActiveApnString(String apnType) {
-        if (VDBG) log( "get active apn string for type:" + apnType);
-        ApnSetting setting = getActiveApnSetting(apnType);
-        return (setting != null) ? setting.getApnName() : null;
-    }
-
-    /**
-     * Returns {@link DctConstants.State} based on the state of the {@link DataConnection} that
-     * contains a {@link ApnSetting} that supported the given apn type {@code anpType}.
-     *
-     * <p>
-     * Assumes there is less than one {@link ApnSetting} can support the given apn type.
-     */
-    // TODO: for enterprise this always returns IDLE, which is ok for now since it is never called
-    // for enterprise
-    public DctConstants.State getState(String apnType) {
-        DctConstants.State state = DctConstants.State.IDLE;
-        final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
-        for (DataConnection dc : mDataConnections.values()) {
-            ApnSetting apnSetting = dc.getApnSetting();
-            if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) {
-                if (dc.isActive()) {
-                    state = getBetterConnectionState(state, DctConstants.State.CONNECTED);
-                } else if (dc.isActivating()) {
-                    state = getBetterConnectionState(state, DctConstants.State.CONNECTING);
-                } else if (dc.isInactive()) {
-                    state = getBetterConnectionState(state, DctConstants.State.IDLE);
-                } else if (dc.isDisconnecting()) {
-                    state = getBetterConnectionState(state, DctConstants.State.DISCONNECTING);
-                }
-            }
-        }
-        return state;
-    }
-
-    /**
-     * Return a better connection state between {@code stateA} and {@code stateB}. Check
-     * {@link #DATA_CONNECTION_STATE_PRIORITIES} for the details.
-     * @return the better connection state between {@code stateA} and {@code stateB}.
-     */
-    private static DctConstants.State getBetterConnectionState(
-            DctConstants.State stateA, DctConstants.State stateB) {
-        int idxA = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateA);
-        int idxB = ArrayUtils.indexOf(DATA_CONNECTION_STATE_PRIORITIES, stateB);
-        return idxA >= idxB ? stateA : stateB;
-    }
-
-    // Return if apn type is a provisioning apn.
-    private boolean isProvisioningApn(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext != null) {
-            return apnContext.isProvisioningApn();
-        }
-        return false;
-    }
-
-    //****** Called from ServiceStateTracker
-    /**
-     * Invoked when ServiceStateTracker observes a transition from GPRS
-     * attach to detach.
-     */
-    private void onDataConnectionDetached() {
-        /*
-         * We presently believe it is unnecessary to tear down the PDP context
-         * when GPRS detaches, but we should stop the network polling.
-         */
-        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
-        stopNetStatPoll();
-        stopDataStallAlarm();
-        mAttached.set(false);
-    }
-
-    private void onDataConnectionAttached() {
-        if (DBG) log("onDataConnectionAttached");
-        mAttached.set(true);
-        if (isAnyDataConnected()) {
-            if (DBG) log("onDataConnectionAttached: start polling notify attached");
-            startNetStatPoll();
-            startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-        }
-        if (mAutoAttachOnCreationConfig) {
-            mAutoAttachEnabled.set(true);
-        }
-        setupDataOnAllConnectableApns(Phone.REASON_DATA_ATTACHED, RetryFailures.ALWAYS);
-    }
-
-    /**
-     * Check if it is allowed to make a data connection (without checking APN context specific
-     * conditions).
-     *
-     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
-     *                              param. It's okay to pass null here and no reasons will be
-     *                              provided.
-     * @return True if data connection is allowed, otherwise false.
-     */
-    public boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
-        return isDataAllowed(null, REQUEST_TYPE_NORMAL, dataConnectionReasons);
-    }
-
-    /**
-     * Check if it is allowed to make a data connection for a given APN type.
-     *
-     * @param apnContext APN context. If passing null, then will only check general but not APN
-     *                   specific conditions (e.g. APN state, metered/unmetered APN).
-     * @param requestType Setup data request type.
-     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
-     *                              param. It's okay to pass null here and no reasons will be
-     *                              provided.
-     * @return True if data connection is allowed, otherwise false.
-     */
-    public boolean isDataAllowed(ApnContext apnContext, @RequestNetworkType int requestType,
-                                 DataConnectionReasons dataConnectionReasons) {
-        // Step 1: Get all environment conditions.
-        // Step 2: Special handling for emergency APN.
-        // Step 3. Build disallowed reasons.
-        // Step 4: Determine if data should be allowed in some special conditions.
-
-        DataConnectionReasons reasons = new DataConnectionReasons();
-
-        int requestApnType = 0;
-        if (apnContext != null) {
-            requestApnType = apnContext.getApnTypeBitmask();
-        }
-
-        // Step 1: Get all environment conditions.
-        final boolean internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
-        boolean attachedState = mAttached.get();
-        boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
-        boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
-        // TODO: Remove this hack added by ag/641832.
-        int dataRat = getDataRat();
-        if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
-            desiredPowerState = true;
-            radioStateFromCarrier = true;
-        }
-
-        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(
-                SubscriptionManager.getDefaultDataSubscriptionId());
-
-        boolean isMeteredApnType = apnContext == null
-                || ApnSettingUtils.isMeteredApnType(requestApnType, mPhone);
-
-        PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
-        // Note this is explicitly not using mPhone.getState.  See b/19090488.
-        // mPhone.getState reports the merge of CS and PS (volte) voice call state
-        // but we only care about CS calls here for data/voice concurrency issues.
-        // Calling getCallTracker currently gives you just the CS side where the
-        // ImsCallTracker is held internally where applicable.
-        // This should be redesigned to ask explicitly what we want:
-        // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
-        if (mPhone.getCallTracker() != null) {
-            phoneState = mPhone.getCallTracker().getState();
-        }
-
-        // Step 2: Special handling for emergency APN.
-        if (apnContext != null
-                && requestApnType == ApnSetting.TYPE_EMERGENCY
-                && apnContext.isConnectable()) {
-            // If this is an emergency APN, as long as the APN is connectable, we
-            // should allow it.
-            if (dataConnectionReasons != null) {
-                dataConnectionReasons.add(DataAllowedReasonType.EMERGENCY_APN);
-            }
-            // Bail out without further checks.
-            return true;
-        }
-
-        // Step 3. Build disallowed reasons.
-        if (apnContext != null && !apnContext.isConnectable()) {
-            DctConstants.State state = apnContext.getState();
-            if (state == DctConstants.State.CONNECTED) {
-                reasons.add(DataDisallowedReasonType.DATA_ALREADY_CONNECTED);
-            } else if (state == DctConstants.State.DISCONNECTING) {
-                reasons.add(DataDisallowedReasonType.DATA_IS_DISCONNECTING);
-            } else if (state == DctConstants.State.CONNECTING) {
-                reasons.add(DataDisallowedReasonType.DATA_IS_CONNECTING);
-            } else {
-                reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
-            }
-        }
-
-        // In legacy mode, if RAT is IWLAN then don't allow default/IA PDP at all.
-        // Rest of APN types can be evaluated for remaining conditions.
-        if ((apnContext != null && requestApnType == ApnSetting.TYPE_DEFAULT
-                || requestApnType == ApnSetting.TYPE_ENTERPRISE
-                || requestApnType == ApnSetting.TYPE_IA)
-                && mPhone.getAccessNetworksManager().isInLegacyMode()
-                && dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
-            reasons.add(DataDisallowedReasonType.ON_IWLAN);
-        }
-
-        // If device is not on NR, don't allow enterprise
-        if (apnContext != null && requestApnType == ApnSetting.TYPE_ENTERPRISE
-                && dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_NR) {
-            reasons.add(DataDisallowedReasonType.NOT_ON_NR);
-        }
-
-        if (shouldRestrictDataForEcbm() || mPhone.isInEmergencyCall()) {
-            reasons.add(DataDisallowedReasonType.IN_ECBM);
-        }
-
-        if (!attachedState && !shouldAutoAttach() && requestType != REQUEST_TYPE_HANDOVER) {
-            reasons.add(DataDisallowedReasonType.NOT_ATTACHED);
-        }
-        if (mPhone.getSubId() == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            reasons.add(DataDisallowedReasonType.SIM_NOT_READY);
-        }
-        if (phoneState != PhoneConstants.State.IDLE
-                && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-            reasons.add(DataDisallowedReasonType.INVALID_PHONE_STATE);
-            reasons.add(DataDisallowedReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
-        }
-        if (!internalDataEnabled) {
-            reasons.add(DataDisallowedReasonType.INTERNAL_DATA_DISABLED);
-        }
-        if (!defaultDataSelected) {
-            reasons.add(DataDisallowedReasonType.DEFAULT_DATA_UNSELECTED);
-        }
-        if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) {
-            reasons.add(DataDisallowedReasonType.ROAMING_DISABLED);
-        }
-        if (mIsPsRestricted) {
-            reasons.add(DataDisallowedReasonType.PS_RESTRICTED);
-        }
-        if (!desiredPowerState) {
-            reasons.add(DataDisallowedReasonType.UNDESIRED_POWER_STATE);
-        }
-        if (!radioStateFromCarrier) {
-            reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
-        }
-        if (!mDataServiceBound) {
-            reasons.add(DataDisallowedReasonType.DATA_SERVICE_NOT_READY);
-        }
-
-        if (apnContext != null) {
-            if (mPhone.getAccessNetworksManager().getPreferredTransport(
-                    apnContext.getApnTypeBitmask())
-                    == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
-                // If QNS explicitly specified this APN type is not allowed on either cellular or
-                // IWLAN, we should not allow data setup.
-                reasons.add(DataDisallowedReasonType.DISABLED_BY_QNS);
-            } else if (mTransportType != mPhone.getAccessNetworksManager().getPreferredTransport(
-                    apnContext.getApnTypeBitmask())) {
-                // If the latest preference has already switched to other transport, we should not
-                // allow data setup.
-                reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
-            }
-
-            // If the transport has been already switched to the other transport, we should not
-            // allow the data setup. The only exception is the handover case, where we setup
-            // handover data connection before switching the transport.
-            if (mTransportType != mPhone.getAccessNetworksManager().getCurrentTransport(
-                    apnContext.getApnTypeBitmask()) && requestType != REQUEST_TYPE_HANDOVER) {
-                reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
-            }
-
-            // Check if the device is under data throttling.
-            long retryTime = mDataThrottler.getRetryTime(apnContext.getApnTypeBitmask());
-            if (retryTime > SystemClock.elapsedRealtime()) {
-                reasons.add(DataDisallowedReasonType.DATA_THROTTLED);
-            }
-        }
-
-        boolean isDataEnabled = apnContext == null ? mDataEnabledSettings.isDataEnabled()
-                : mDataEnabledSettings.isDataEnabled(requestApnType);
-
-        if (!isDataEnabled) {
-            reasons.add(DataDisallowedReasonType.DATA_DISABLED);
-        }
-
-        // If there are hard disallowed reasons, we should not allow data connection no matter what.
-        if (reasons.containsHardDisallowedReasons()) {
-            if (dataConnectionReasons != null) {
-                dataConnectionReasons.copyFrom(reasons);
-            }
-            return false;
-        }
-
-        // Step 4: Determine if data should be allowed in some special conditions.
-
-        // At this point, if data is not allowed, it must be because of the soft reasons. We
-        // should start to check some special conditions that data will be allowed.
-        if (!reasons.allowed()) {
-            // Check if the transport is WLAN ie wifi (for AP-assisted mode devices)
-            if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                reasons.add(DataAllowedReasonType.UNMETERED_APN);
-            // Or if the data is on cellular, and the APN type is determined unmetered by the
-            // configuration.
-            } else if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                    && !isMeteredApnType && requestApnType != ApnSetting.TYPE_DEFAULT
-                    && requestApnType != ApnSetting.TYPE_ENTERPRISE) {
-                reasons.add(DataAllowedReasonType.UNMETERED_APN);
-            }
-
-            // If the request is restricted and there are only soft disallowed reasons (e.g. data
-            // disabled, data roaming disabled) existing, we should allow the data. ENTERPRISE is
-            // an exception and should not be treated as restricted for this purpose; it should be
-            // treated same as DEFAULT.
-            if (apnContext != null
-                    && apnContext.hasRestrictedRequests(true)
-                    && !apnContext.getApnType().equals(ApnSetting.TYPE_ENTERPRISE_STRING)
-                    && !reasons.allowed()) {
-                reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
-            }
-        } else {
-            // If there is no disallowed reasons, then we should allow the data request with
-            // normal reason.
-            reasons.add(DataAllowedReasonType.NORMAL);
-        }
-
-        if (dataConnectionReasons != null) {
-            dataConnectionReasons.copyFrom(reasons);
-        }
-
-        return reasons.allowed();
-    }
-
-    // arg for setupDataOnAllConnectableApns
-    protected enum RetryFailures {
-        // retry failed networks always (the old default)
-        ALWAYS,
-        // retry only when a substantial change has occurred.  Either:
-        // 1) we were restricted by voice/data concurrency and aren't anymore
-        // 2) our apn list has change
-        ONLY_ON_CHANGE
-    };
-
-    protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
-        if (VDBG) log("setupDataOnAllConnectableApns: " + reason);
-
-        if (DBG && !VDBG) {
-            StringBuilder sb = new StringBuilder(120);
-            for (ApnContext apnContext : mPrioritySortedApnContexts) {
-                sb.append(apnContext.getApnType());
-                sb.append(":[state=");
-                sb.append(apnContext.getState());
-                sb.append(",enabled=");
-                sb.append(apnContext.isEnabled());
-                sb.append("] ");
-            }
-            log("setupDataOnAllConnectableApns: " + reason + " " + sb);
-        }
-
-        for (ApnContext apnContext : mPrioritySortedApnContexts) {
-            setupDataOnConnectableApn(apnContext, reason, retryFailures);
-        }
-    }
-
-    protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
-            RetryFailures retryFailures) {
-        if (VDBG) log("setupDataOnAllConnectableApns: apnContext " + apnContext);
-
-        if (apnContext.getState() == DctConstants.State.FAILED
-                || apnContext.getState() == DctConstants.State.RETRYING) {
-            if (retryFailures == RetryFailures.ALWAYS) {
-                apnContext.releaseDataConnection(reason);
-            } else if (!apnContext.isConcurrentVoiceAndDataAllowed()
-                    && mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-                // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
-                apnContext.releaseDataConnection(reason);
-            }
-        }
-        if (apnContext.isConnectable()) {
-            log("isConnectable() call trySetupData");
-            apnContext.setReason(reason);
-            trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
-        }
-    }
-
-    private boolean shouldRestrictDataForEcbm() {
-        boolean isInEcm = mPhone.isInEcm();
-        boolean isInImsEcm = mPhone.getImsPhone() != null && mPhone.getImsPhone().isInImsEcm();
-        log("shouldRestrictDataForEcbm: isInEcm=" + isInEcm + " isInImsEcm=" + isInImsEcm);
-        return isInEcm && !isInImsEcm;
-    }
-
-    private boolean isHandoverPending(@ApnType int apnType) {
-        List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
-        return messageList != null && messageList.size() > 0;
-    }
-
-    private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType,
-            @Nullable Message onHandoverCompleteMsg) {
-        if (onHandoverCompleteMsg != null) {
-            addHandoverCompleteMsg(onHandoverCompleteMsg, apnContext.getApnTypeBitmask());
-        }
-
-        if (mPhone.getSimulatedRadioControl() != null) {
-            // Assume data is connected on the simulator
-            log("trySetupData: X We're on the simulator; assuming connected retValue=true");
-            return;
-        }
-
-        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-        boolean isDataAllowed = isDataAllowed(apnContext, requestType, dataConnectionReasons);
-        String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
-                + apnContext.getReason() + ", requestType=" + requestTypeToString(requestType)
-                + ". " + dataConnectionReasons.toString();
-        if (dataConnectionReasons.contains(DataDisallowedReasonType.DISABLED_BY_QNS)
-                || dataConnectionReasons.contains(DataDisallowedReasonType.ON_OTHER_TRANSPORT)) {
-            logStr += ", current transport=" + AccessNetworkConstants.transportTypeToString(
-                    mPhone.getAccessNetworksManager().getCurrentTransport(
-                            apnContext.getApnTypeBitmask()));
-            logStr += ", preferred transport=" + AccessNetworkConstants.transportTypeToString(
-                    mPhone.getAccessNetworksManager().getPreferredTransport(
-                            apnContext.getApnTypeBitmask()));
-        }
-        if (DBG) log(logStr);
-        ApnContext.requestLog(apnContext, logStr);
-        if (!isDataAllowed) {
-            StringBuilder str = new StringBuilder();
-
-            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
-                    + ", mState=" + apnContext.getState() + ", apnEnabled="
-                    + apnContext.isEnabled() + ", mDependencyMet="
-                    + apnContext.isDependencyMet() + "] ");
-
-            if (!mDataEnabledSettings.isDataEnabled()) {
-                str.append("isDataEnabled() = false. " + mDataEnabledSettings);
-            }
-
-            // Check if it fails because of the existing data is still disconnecting.
-            if (dataConnectionReasons.contains(DataDisallowedReasonType.DATA_IS_DISCONNECTING)
-                    && isHandoverPending(apnContext.getApnTypeBitmask())) {
-                // Normally we don't retry when isDataAllow() returns false, because that's consider
-                // pre-condition not met, for example, data not enabled by the user, or airplane
-                // mode is on. If we retry in those cases, there will be significant power impact.
-                // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover
-                // case only.
-                log("Data is disconnecting. Will retry handover later.");
-                return;
-            }
-
-            // If this is a data retry, we should set the APN state to FAILED so it won't stay
-            // in RETRYING forever.
-            if (apnContext.getState() == DctConstants.State.RETRYING) {
-                apnContext.setState(DctConstants.State.FAILED);
-                str.append(" Stop retrying.");
-            }
-
-            if (DBG) log(str.toString());
-            ApnContext.requestLog(apnContext, str.toString());
-            if (requestType == REQUEST_TYPE_HANDOVER) {
-                // If fails due to latest preference already changed back to source transport, then
-                // just fallback (will not attempt handover anymore, and will not tear down the
-                // data connection on source transport.
-                boolean fallback = dataConnectionReasons.contains(
-                        DataDisallowedReasonType.ON_OTHER_TRANSPORT);
-                sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, fallback);
-            }
-            return;
-        }
-
-        if (apnContext.getState() == DctConstants.State.FAILED) {
-            String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
-            if (DBG) log(str);
-            ApnContext.requestLog(apnContext, str);
-            apnContext.setState(DctConstants.State.IDLE);
-        }
-        int radioTech = getDataRat();
-        if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState()
-                .getState() == ServiceState.STATE_IN_SERVICE) {
-            radioTech = getVoiceRat();
-        }
-        log("service state=" + mPhone.getServiceState());
-        apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
-                .isConcurrentVoiceAndDataAllowed());
-        if (apnContext.getState() == DctConstants.State.IDLE) {
-            ArrayList<ApnSetting> waitingApns =
-                    buildWaitingApns(apnContext.getApnType(), radioTech);
-            if (waitingApns.isEmpty()) {
-                String str = "trySetupData: X No APN found retValue=false";
-                if (DBG) log(str);
-                ApnContext.requestLog(apnContext, str);
-                if (requestType == REQUEST_TYPE_HANDOVER) {
-                    sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
-                            false);
-                }
-                return;
-            } else {
-                apnContext.setWaitingApns(waitingApns);
-                if (DBG) {
-                    log("trySetupData: Create from mAllApnSettings : "
-                                + apnListToString(mAllApnSettings));
-                }
-            }
-        }
-
-        if (!setupData(apnContext, radioTech, requestType)
-                && requestType == REQUEST_TYPE_HANDOVER) {
-            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
-        }
-    }
-
-    /**
-     * Clean up all data connections. Note this is just detach the APN context from the data
-     * connection. After all APN contexts are detached from the data connection, the data
-     * connection will be torn down.
-     *
-     * @param reason Reason for the clean up.
-     */
-    public void cleanUpAllConnections(String reason) {
-        log("cleanUpAllConnections");
-        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS);
-        msg.obj = reason;
-        sendMessage(msg);
-    }
-
-    /**
-     * Clean up all data connections by detaching the APN contexts from the data connections, which
-     * eventually tearing down all data connections after all APN contexts are detached from the
-     * data connections.
-     *
-     * @param detach {@code true} if detaching APN context from the underlying data connection (when
-     * no other APN context is attached to the data connection, the data connection will be torn
-     * down.) {@code false} to only reset the data connection's state machine.
-     *
-     * @param reason reason for the clean up.
-     * @return boolean - true if we did cleanup any connections, false if they
-     *                   were already all disconnected.
-     */
-    private boolean cleanUpAllConnectionsInternal(boolean detach, String reason) {
-        if (DBG) log("cleanUpAllConnectionsInternal: detach=" + detach + " reason=" + reason);
-        boolean didDisconnect = false;
-        boolean disableMeteredOnly = false;
-
-        // reasons that only metered apn will be torn down
-        if (!TextUtils.isEmpty(reason)) {
-            disableMeteredOnly = reason.equals(Phone.REASON_DATA_SPECIFIC_DISABLED) ||
-                    reason.equals(Phone.REASON_ROAMING_ON) ||
-                    reason.equals(Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
-        }
-
-        for (ApnContext apnContext : mApnContexts.values()) {
-            // Exclude the IMS APN from single data connection case.
-            if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
-                    && apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
-                continue;
-            }
-
-            if (shouldCleanUpConnection(apnContext, disableMeteredOnly,
-                    reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION))) {
-                // TODO - only do cleanup if not disconnected
-                if (apnContext.isDisconnected() == false) didDisconnect = true;
-                apnContext.setReason(reason);
-                cleanUpConnectionInternal(detach, RELEASE_TYPE_DETACH, apnContext);
-            } else if (DBG) {
-                log("cleanUpAllConnectionsInternal: APN type " + apnContext.getApnType()
-                        + " shouldn't be cleaned up.");
-            }
-        }
-
-        stopNetStatPoll();
-        stopDataStallAlarm();
-
-        // TODO: Do we need mRequestedApnType?
-        mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
-        if (areAllDataDisconnected()) {
-            notifyAllDataDisconnected();
-        }
-
-        return didDisconnect;
-    }
-
-    boolean shouldCleanUpConnection(ApnContext apnContext, boolean disableMeteredOnly,
-            boolean singlePdn) {
-        if (apnContext == null) return false;
-
-        // If APN setting is not null and the reason is single PDN arbitration, clean up connection.
-        ApnSetting apnSetting = apnContext.getApnSetting();
-        if (apnSetting != null && singlePdn) return true;
-
-        // If meteredOnly is false, clean up all connections.
-        if (!disableMeteredOnly) return true;
-
-        // If meteredOnly is true, and apnSetting is null or it's un-metered, no need to clean up.
-        if (apnSetting == null || !ApnSettingUtils.isMetered(apnSetting, mPhone)) return false;
-
-        boolean isRoaming = mPhone.getServiceState().getDataRoaming();
-        boolean isDataRoamingDisabled = !getDataRoamingEnabled();
-        boolean isDataDisabled = !mDataEnabledSettings.isDataEnabled(
-                apnSetting.getApnTypeBitmask());
-
-        // Should clean up if its data is disabled, or data roaming is disabled while roaming.
-        return isDataDisabled || (isRoaming && isDataRoamingDisabled);
-    }
-
-    /**
-     * Detach the APN context from the associated data connection. This data connection might be
-     * torn down if no other APN context is attached to it.
-     *
-     * @param apnContext The APN context to be detached
-     */
-    void cleanUpConnection(ApnContext apnContext) {
-        if (DBG) log("cleanUpConnection: apnContext=" + apnContext);
-        Message msg = obtainMessage(DctConstants.EVENT_CLEAN_UP_CONNECTION);
-        msg.arg2 = 0;
-        msg.obj = apnContext;
-        sendMessage(msg);
-    }
-
-    /**
-     * Detach the APN context from the associated data connection. This data connection will be
-     * torn down if no other APN context is attached to it.
-     *
-     * @param detach {@code true} if detaching APN context from the underlying data connection (when
-     * no other APN context is attached to the data connection, the data connection will be torn
-     * down.) {@code false} to only reset the data connection's state machine.
-     * @param releaseType Data release type.
-     * @param apnContext The APN context to be detached.
-     */
-    private void cleanUpConnectionInternal(boolean detach, @ReleaseNetworkType int releaseType,
-                                           ApnContext apnContext) {
-        if (apnContext == null) {
-            if (DBG) log("cleanUpConnectionInternal: apn context is null");
-            return;
-        }
-
-        DataConnection dataConnection = apnContext.getDataConnection();
-        String str = "cleanUpConnectionInternal: detach=" + detach + " reason="
-                + apnContext.getReason();
-        if (VDBG) log(str + " apnContext=" + apnContext);
-        ApnContext.requestLog(apnContext, str);
-        if (detach) {
-            if (apnContext.isDisconnected()) {
-                // The request is detach and but ApnContext is not connected.
-                // If apnContext is not enabled anymore, break the linkage to the data connection.
-                apnContext.releaseDataConnection("");
-            } else {
-                // Connection is still there. Try to clean up.
-                if (dataConnection != null) {
-                    if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
-                        boolean disconnectAll = false;
-                        if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
-                                && ServiceState.isCdma(getDataRat())) {
-                            if (DBG) {
-                                log("cleanUpConnectionInternal: disconnectAll DUN connection");
-                            }
-                            // For CDMA DUN, we need to tear it down immediately. A new data
-                            // connection will be reestablished with correct profile id.
-                            disconnectAll = true;
-                        }
-                        final int generation = apnContext.getConnectionGeneration();
-                        str = "cleanUpConnectionInternal: tearing down"
-                                + (disconnectAll ? " all" : "") + " using gen#" + generation;
-                        if (DBG) log(str + "apnContext=" + apnContext);
-                        ApnContext.requestLog(apnContext, str);
-                        Pair<ApnContext, Integer> pair = new Pair<>(apnContext, generation);
-                        Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, pair);
-
-                        if (disconnectAll || releaseType == RELEASE_TYPE_HANDOVER) {
-                            dataConnection.tearDownAll(apnContext.getReason(), releaseType, msg);
-                        } else {
-                            dataConnection.tearDown(apnContext, apnContext.getReason(), msg);
-                        }
-
-                        apnContext.setState(DctConstants.State.DISCONNECTING);
-                    }
-                } else {
-                    // apn is connected but no reference to the data connection.
-                    // Should not be happen, but reset the state in case.
-                    apnContext.setState(DctConstants.State.IDLE);
-                    ApnContext.requestLog(
-                            apnContext, "cleanUpConnectionInternal: connected, bug no dc");
-                }
-            }
-        } else {
-            // force clean up the data connection.
-            if (dataConnection != null) dataConnection.reset();
-            apnContext.setState(DctConstants.State.IDLE);
-            apnContext.setDataConnection(null);
-        }
-
-        // If there is any outstanding handover request, we need to respond it.
-        sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
-
-        // Make sure reconnection alarm is cleaned up if there is no ApnContext
-        // associated to the connection.
-        if (dataConnection != null) {
-            cancelReconnect(apnContext);
-        }
-        str = "cleanUpConnectionInternal: X detach=" + detach + " reason="
-                + apnContext.getReason();
-        if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
-    }
-
-    private Cursor getPreferredApnCursor(int subId) {
-        Cursor cursor = null;
-        if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            cursor = mPhone.getContext().getContentResolver().query(
-                    Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID,
-                            String.valueOf(subId)), null, null, null,
-                    Telephony.Carriers.DEFAULT_SORT_ORDER);
-        }
-        return cursor;
-    }
-
-    private ApnSetting getPreferredApnFromDB() {
-        ApnSetting preferredApn = null;
-        Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
-        if (cursor != null) {
-            if (cursor.getCount() > 0) {
-                cursor.moveToFirst();
-                preferredApn = ApnSetting.makeApnSetting(cursor);
-            }
-            cursor.close();
-        }
-        if (VDBG) log("getPreferredApnFromDB: preferredApn=" + preferredApn);
-        return preferredApn;
-    }
-
-    private void setDefaultPreferredApnIfNeeded() {
-        ApnSetting defaultPreferredApn = null;
-        PersistableBundle bundle = getCarrierConfig();
-        String defaultPreferredApnName = bundle.getString(CarrierConfigManager
-                .KEY_DEFAULT_PREFERRED_APN_NAME_STRING);
-
-        if (TextUtils.isEmpty(defaultPreferredApnName) || getPreferredApnFromDB() != null) {
-            return;
-        }
-
-        String selection = Telephony.Carriers.APN + " = \"" + defaultPreferredApnName + "\" AND "
-                + Telephony.Carriers.EDITED_STATUS + " = " + Telephony.Carriers.UNEDITED;
-        Cursor cursor = mPhone.getContext().getContentResolver().query(
-                Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
-                        "filtered/subId/" + mPhone.getSubId()),
-                null, selection, null, Telephony.Carriers._ID);
-
-        if (cursor != null) {
-            if (cursor.getCount() > 0) {
-                if (cursor.moveToFirst()) {
-                    defaultPreferredApn = ApnSetting.makeApnSetting(cursor);
-                }
-            }
-            cursor.close();
-        }
-
-        if (defaultPreferredApn != null
-                && defaultPreferredApn.canHandleType(mRequestedApnType)) {
-            log("setDefaultPreferredApnIfNeeded: For APN type "
-                    + ApnSetting.getApnTypeString(mRequestedApnType)
-                    + " found default apnSetting "
-                    + defaultPreferredApn);
-
-            setPreferredApn(defaultPreferredApn.getId(), true);
-        }
-
-        return;
-    }
-
-    /**
-     * Check if preferred apn is allowed to edit by user.
-     * @return {@code true} if it is allowed to edit.
-     */
-    @VisibleForTesting
-    public boolean isPreferredApnUserEdited() {
-        boolean isUserEdited = false;
-        Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
-        if (cursor != null) {
-            if (cursor.getCount() > 0) {
-                if (cursor.moveToFirst()) {
-                    isUserEdited = cursor.getInt(
-                            cursor.getColumnIndexOrThrow(Telephony.Carriers.EDITED_STATUS))
-                            == Telephony.Carriers.USER_EDITED;
-                }
-            }
-            cursor.close();
-        }
-        if (VDBG) log("isPreferredApnUserEdited: isUserEdited=" + isUserEdited);
-        return isUserEdited;
-    }
-
-    /**
-     * Fetch the DUN apns
-     * @return a list of DUN ApnSetting objects
-     */
-    @VisibleForTesting
-    public @NonNull ArrayList<ApnSetting> fetchDunApns() {
-        if (mPhone.getServiceState().getRoaming() && !isPreferredApnUserEdited()
-                && getCarrierConfig().getBoolean(CarrierConfigManager
-                .KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL)) {
-            if (VDBG) log("fetchDunApns: Dun apn is not used in roaming network");
-            return new ArrayList<ApnSetting>(0);
-        }
-
-        int bearer = getDataRat();
-        ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
-        ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
-
-        if (dunCandidates.isEmpty()) {
-            if (!ArrayUtils.isEmpty(mAllApnSettings)) {
-                for (ApnSetting apn : mAllApnSettings) {
-                    if (apn.canHandleType(ApnSetting.TYPE_DUN)) {
-                        dunCandidates.add(apn);
-                    }
-                }
-                if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates);
-            }
-        }
-
-        int preferredApnSetId = getPreferredApnSetId();
-        ApnSetting preferredApn = getPreferredApnFromDB();
-        for (ApnSetting dunSetting : dunCandidates) {
-            if (dunSetting.canSupportNetworkType(
-                    ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
-                if (preferredApnSetId == dunSetting.getApnSetId()) {
-                    if (preferredApn != null && preferredApn.equals(dunSetting)) {
-                        // If there is a preferred APN can handled DUN type, prepend it to list to
-                        // use it preferred.
-                        retDunSettings.add(0, dunSetting);
-                    } else {
-                        retDunSettings.add(dunSetting);
-                    }
-                }
-            }
-        }
-
-        if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
-        return retDunSettings;
-    }
-
-    private int getPreferredApnSetId() {
-        // preferapnset uri returns all APNs for the current carrier which have an apn_set_id
-        // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id,
-        // the query will return null)
-        Cursor c = mPhone.getContext().getContentResolver()
-                .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
-                    "preferapnset/subId/" + mPhone.getSubId()),
-                        new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null);
-        if (c == null) {
-            loge("getPreferredApnSetId: cursor is null");
-            return Telephony.Carriers.NO_APN_SET_ID;
-        }
-
-        int setId;
-        if (c.getCount() < 1) {
-            loge("getPreferredApnSetId: no APNs found");
-            setId = Telephony.Carriers.NO_APN_SET_ID;
-        } else {
-            c.moveToFirst();
-            setId = c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
-        }
-
-        if (!c.isClosed()) {
-            c.close();
-        }
-        return setId;
-    }
-
-    public boolean hasMatchedTetherApnSetting() {
-        ArrayList<ApnSetting> matches = fetchDunApns();
-        log("hasMatchedTetherApnSetting: APNs=" + matches);
-        return matches.size() > 0;
-    }
-
-    /**
-     * @return the {@link DataConnection} with the given context id {@code cid}.
-     */
-    public DataConnection getDataConnectionByContextId(int cid) {
-        return mDcc.getActiveDcByCid(cid);
-    }
-
-    /**
-     * @return the {@link DataConnection} with the given APN context. Null if no data connection
-     * is found.
-     */
-    public @Nullable DataConnection getDataConnectionByApnType(String apnType) {
-        // TODO: Clean up all APN type in string usage
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext != null) {
-            return apnContext.getDataConnection();
-        }
-        return null;
-    }
-
-    /**
-     * Check if the data fail cause is a permanent failure (i.e. Frameworks will not retry data
-     * setup).
-     *
-     * @param dcFailCause The data fail cause
-     * @return {@code true} if the data fail cause is a permanent failure.
-     */
-    @VisibleForTesting
-    public boolean isPermanentFailure(@DataFailureCause int dcFailCause) {
-        return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause,
-                mPhone.getSubId())
-                && (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST));
-    }
-
-    private DataConnection findFreeDataConnection() {
-        for (DataConnection dataConnection : mDataConnections.values()) {
-            boolean inUse = false;
-            for (ApnContext apnContext : mApnContexts.values()) {
-                if (apnContext.getDataConnection() == dataConnection) {
-                    inUse = true;
-                    break;
-                }
-            }
-            if (!inUse) {
-                if (DBG) {
-                    log("findFreeDataConnection: found free DataConnection=" + dataConnection);
-                }
-                return dataConnection;
-            }
-        }
-        log("findFreeDataConnection: NO free DataConnection");
-        return null;
-    }
-
-    /**
-     * Setup a data connection based on given APN type.
-     *
-     * @param apnContext APN context
-     * @param radioTech RAT of the data connection
-     * @param requestType Data request type
-     * @return True if successful, otherwise false.
-     */
-    private boolean setupData(ApnContext apnContext, int radioTech,
-                              @RequestNetworkType int requestType) {
-        if (DBG) {
-            log("setupData: apnContext=" + apnContext + ", requestType="
-                    + requestTypeToString(requestType));
-        }
-        ApnContext.requestLog(
-                apnContext, "setupData. requestType=" + requestTypeToString(requestType));
-        ApnSetting apnSetting;
-        DataConnection dataConnection = null;
-
-        apnSetting = apnContext.getNextApnSetting();
-
-        if (apnSetting == null) {
-            if (DBG) log("setupData: return for no apn found!");
-            return false;
-        }
-
-        // profile id is only meaningful when the profile is persistent on the modem.
-        int profileId = DATA_PROFILE_INVALID;
-        if (apnSetting.isPersistent()) {
-            profileId = apnSetting.getProfileId();
-            if (profileId == DATA_PROFILE_DEFAULT) {
-                profileId = getApnProfileID(apnContext.getApnType());
-            }
-        }
-
-        // On CDMA, if we're explicitly asking for DUN, we need have
-        // a dun-profiled connection so we can't share an existing one
-        // On GSM/LTE we can share existing apn connections provided they support
-        // this type.
-        // If asking for ENTERPRISE, there are no compatible data connections, so skip this check
-        if ((apnContext.getApnTypeBitmask() != ApnSetting.TYPE_DUN
-                || ServiceState.isGsm(getDataRat()))
-                && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE) {
-            dataConnection = checkForCompatibleDataConnection(apnContext, apnSetting);
-            if (dataConnection != null) {
-                // Get the apn setting used by the data connection
-                ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting();
-                if (dataConnectionApnSetting != null) {
-                    // Setting is good, so use it.
-                    apnSetting = dataConnectionApnSetting;
-                }
-            }
-        }
-        if (dataConnection == null) {
-            if (isOnlySingleDcAllowed(radioTech)) {
-                if (isHigherPriorityApnContextActive(apnContext)) {
-                    if (DBG) {
-                        log("setupData: Higher priority ApnContext active.  Ignoring call");
-                    }
-                    return false;
-                }
-
-                // Should not start cleanUp if the setupData is for IMS APN
-                // or retry of same APN(State==RETRYING).
-                if (!apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)
-                        && (apnContext.getState() != DctConstants.State.RETRYING)) {
-                    // Only lower priority calls left.  Disconnect them all in this single PDP case
-                    // so that we can bring up the requested higher priority call (once we receive
-                    // response for deactivate request for the calls we are about to disconnect
-                    if (cleanUpAllConnectionsInternal(true, Phone.REASON_SINGLE_PDN_ARBITRATION)) {
-                        // If any call actually requested to be disconnected, means we can't
-                        // bring up this connection yet as we need to wait for those data calls
-                        // to be disconnected.
-                        if (DBG) log("setupData: Some calls are disconnecting first."
-                                + " Wait and retry");
-                        return false;
-                    }
-                }
-
-                // No other calls are active, so proceed
-                if (DBG) log("setupData: Single pdp. Continue setting up data call.");
-            }
-
-            dataConnection = findFreeDataConnection();
-
-            if (dataConnection == null) {
-                dataConnection = createDataConnection();
-            }
-
-            if (dataConnection == null) {
-                if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
-                return false;
-            }
-        }
-        final int generation = apnContext.incAndGetConnectionGeneration();
-        if (DBG) {
-            log("setupData: dc=" + dataConnection + " apnSetting=" + apnSetting + " gen#="
-                    + generation);
-        }
-
-        apnContext.setDataConnection(dataConnection);
-        apnContext.setApnSetting(apnSetting);
-        apnContext.setState(DctConstants.State.CONNECTING);
-
-        Message msg = obtainMessage();
-        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
-        msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
-
-        ApnSetting preferredApn = getPreferredApn();
-        boolean isPreferredApn = apnSetting.equals(preferredApn);
-        dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation, requestType,
-                mPhone.getSubId(), isPreferredApn);
-
-        if (DBG) {
-            if (isPreferredApn) {
-                log("setupData: initing! isPreferredApn=" + isPreferredApn
-                        + ", apnSetting={" + apnSetting.toString() + "}");
-            } else {
-                String preferredApnStr = preferredApn == null ? "null" : preferredApn.toString();
-                log("setupData: initing! isPreferredApn=" + isPreferredApn
-                        + ", apnSetting={" + apnSetting + "}"
-                        + ", preferredApn={" + preferredApnStr + "}");
-            }
-        }
-        return true;
-    }
-
-    // Get the allowed APN types for initial attach. The order in the returned list represent
-    // the order of APN types that should be used for initial attach.
-    private @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() {
-        PersistableBundle bundle = getCarrierConfig();
-        if (bundle != null) {
-            String[] apnTypesArray = bundle.getStringArray(
-                    CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY);
-            if (apnTypesArray != null) {
-                return Arrays.stream(apnTypesArray)
-                        .map(ApnSetting::getApnTypesBitmaskFromString)
-                        .collect(Collectors.toList());
-            }
-        }
-
-        return Collections.emptyList();
-    }
-
-    protected void setInitialAttachApn() {
-        ApnSetting apnSetting = null;
-        int preferredApnSetId = getPreferredApnSetId();
-        ArrayList<ApnSetting> allApnSettings = new ArrayList<>();
-        if (mPreferredApn != null) {
-            // Put the preferred apn at the beginning of the list. It's okay to have a duplicate
-            // when later on mAllApnSettings get added. That would not change the selection result.
-            allApnSettings.add(mPreferredApn);
-        }
-        allApnSettings.addAll(mAllApnSettings);
-
-        // Get the allowed APN types for initial attach. Note that if none of the APNs has the
-        // allowed APN types, then the initial attach will not be performed.
-        List<Integer> allowedApnTypes = getAllowedInitialAttachApnTypes();
-        for (int allowedApnType : allowedApnTypes) {
-            apnSetting = allApnSettings.stream()
-                    .filter(apn -> apn.canHandleType(allowedApnType))
-                    .filter(apn -> (apn.getApnSetId() == preferredApnSetId
-                            || apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID))
-                    .findFirst()
-                    .orElse(null);
-            if (apnSetting != null) break;
-        }
-
-        if (DBG) {
-            log("setInitialAttachApn: Allowed APN types=" + allowedApnTypes.stream()
-                    .map(ApnSetting::getApnTypeString)
-                    .collect(Collectors.joining(",")));
-        }
-
-        if (apnSetting == null) {
-            if (DBG) log("setInitialAttachApn: X There in no available apn.");
-        } else {
-            if (DBG) log("setInitialAttachApn: X selected APN=" + apnSetting);
-            mDataServiceManager.setInitialAttachApn(new DataProfile.Builder()
-                    .setApnSetting(apnSetting)
-                    .setPreferred(apnSetting.equals(getPreferredApn()))
-                    .build(),
-                    mPhone.getServiceState().getDataRoamingFromRegistration(), null);
-        }
-    }
-
-    /**
-     * Handles changes to the APN database.
-     */
-    private void onApnChanged() {
-        if (mPhone instanceof GsmCdmaPhone) {
-            // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
-            ((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
-        }
-
-        // TODO: It'd be nice to only do this if the changed entrie(s)
-        // match the current operator.
-        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
-        mDataThrottler.reset();
-        setDefaultPreferredApnIfNeeded();
-        createAllApnList();
-        setDataProfilesAsNeeded();
-        setInitialAttachApn();
-        cleanUpConnectionsOnUpdatedApns(isAnyDataConnected(), Phone.REASON_APN_CHANGED);
-
-        // FIXME: See bug 17426028 maybe no conditional is needed.
-        if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
-            setupDataOnAllConnectableApns(Phone.REASON_APN_CHANGED, RetryFailures.ALWAYS);
-        }
-    }
-
-    /**
-     * "Active" here means ApnContext isEnabled() and not in FAILED state
-     * @param apnContext to compare with
-     * @return true if higher priority active apn found
-     */
-    private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
-        if (apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
-            return false;
-        }
-
-        for (ApnContext otherContext : mPrioritySortedApnContexts) {
-            if (otherContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
-                continue;
-            }
-            if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
-            if (otherContext.isEnabled() && otherContext.getState() != DctConstants.State.FAILED) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Reports if we support multiple connections or not.
-     * This is a combination of factors, based on carrier and RAT.
-     * @param rilRadioTech the RIL Radio Tech currently in use
-     * @return true if only single DataConnection is allowed
-     */
-    private boolean isOnlySingleDcAllowed(int rilRadioTech) {
-        int networkType = ServiceState.rilRadioTechnologyToNetworkType(rilRadioTech);
-        // Default single dc rats with no knowledge of carrier
-        int[] singleDcRats = null;
-        // get the carrier specific value, if it exists, from CarrierConfigManager.
-        // generally configManager and bundle should not be null, but if they are it should be okay
-        // to leave singleDcRats null as well
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
-            if (bundle != null) {
-                singleDcRats = bundle.getIntArray(
-                        CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY);
-            }
-        }
-        boolean onlySingleDcAllowed = false;
-        if (TelephonyUtils.IS_DEBUGGABLE
-                && SystemProperties.getBoolean("persist.telephony.test.singleDc", false)) {
-            onlySingleDcAllowed = true;
-        }
-        if (singleDcRats != null) {
-            for (int i = 0; i < singleDcRats.length && !onlySingleDcAllowed; i++) {
-                if (networkType == singleDcRats[i]) {
-                    onlySingleDcAllowed = true;
-                }
-            }
-        }
-
-        if (DBG) {
-            log("isOnlySingleDcAllowed(" + TelephonyManager.getNetworkTypeName(networkType) + "): "
-                    + onlySingleDcAllowed);
-        }
-        return onlySingleDcAllowed;
-    }
-
-    void sendRestartRadio() {
-        if (DBG)log("sendRestartRadio:");
-        Message msg = obtainMessage(DctConstants.EVENT_RESTART_RADIO);
-        sendMessage(msg);
-    }
-
-    private void restartRadio() {
-        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
-        cleanUpAllConnectionsInternal(true, Phone.REASON_RADIO_TURNED_OFF);
-        mPhone.getServiceStateTracker().powerOffRadioSafely();
-        /* Note: no need to call setRadioPower(true).  Assuming the desired
-         * radio power state is still ON (as tracked by ServiceStateTracker),
-         * ServiceStateTracker will call setRadioPower when it receives the
-         * RADIO_STATE_CHANGED notification for the power off.  And if the
-         * desired power state has changed in the interim, we don't want to
-         * override it with an unconditional power on.
-         */
-    }
-
-    /**
-     * Return true if data connection need to be setup after disconnected due to
-     * reason.
-     *
-     * @param apnContext APN context
-     * @return true if try setup data connection is need for this reason
-     */
-    private boolean retryAfterDisconnected(ApnContext apnContext) {
-        boolean retry = true;
-        String reason = apnContext.getReason();
-
-        if (Phone.REASON_RADIO_TURNED_OFF.equals(reason) || (isOnlySingleDcAllowed(getDataRat())
-                && isHigherPriorityApnContextActive(apnContext))) {
-            retry = false;
-        }
-        return retry;
-    }
-
-    protected void startReconnect(long delay, ApnContext apnContext,
-            @RequestNetworkType int requestType) {
-        apnContext.setState(DctConstants.State.RETRYING);
-        Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
-                       mPhone.getSubId(), requestType, apnContext);
-        cancelReconnect(apnContext);
-
-        // Wait a bit before trying the next APN, so that
-        // we're not tying up the RIL command channel
-        sendMessageDelayed(msg, delay);
-
-        if (DBG) {
-            log("startReconnect: delay=" + delay + ", apn="
-                    + apnContext + ", reason=" + apnContext.getReason()
-                    + ", subId=" + mPhone.getSubId() + ", request type="
-                    + requestTypeToString(requestType));
-        }
-    }
-
-    /**
-     * Cancels the alarm associated with apnContext.
-     *
-     * @param apnContext on which the alarm should be stopped.
-     */
-    protected void cancelReconnect(ApnContext apnContext) {
-        if (apnContext == null) return;
-
-        if (DBG) {
-            log("cancelReconnect: apn=" + apnContext);
-        }
-        removeMessages(DctConstants.EVENT_DATA_RECONNECT, apnContext);
-    }
-
-    /**
-     * Read configuration. Note this must be called after carrier config is ready.
-     */
-    private void readConfiguration() {
-        log("readConfiguration");
-        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-            // Auto attach is for cellular only.
-            mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
-                    .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
-        }
-
-        mAutoAttachEnabled.set(false);
-        setDefaultPreferredApnIfNeeded();
-        read5GConfiguration();
-        registerSettingsObserver();
-        SubscriptionPlan[] plans = mNetworkPolicyManager.getSubscriptionPlans(
-                mPhone.getSubId(), mPhone.getContext().getOpPackageName());
-        mSubscriptionPlans = plans == null ? Collections.emptyList() : Arrays.asList(plans);
-        if (DBG) log("SubscriptionPlans initialized: " + mSubscriptionPlans);
-        reevaluateUnmeteredConnections();
-        mConfigReady = true;
-    }
-
-    /**
-     * @return {@code true} if carrier config has been applied.
-     */
-    private boolean isCarrierConfigApplied() {
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
-            if (b != null) {
-                return CarrierConfigManager.isConfigForIdentifiedCarrier(b);
-            }
-        }
-        return false;
-    }
-
-    private void onCarrierConfigChanged() {
-        if (DBG) log("onCarrierConfigChanged");
-
-        if (!isCarrierConfigApplied()) {
-            log("onCarrierConfigChanged: Carrier config is not ready yet.");
-            return;
-        }
-
-        readConfiguration();
-
-        if (mSimState == TelephonyManager.SIM_STATE_LOADED) {
-            setDefaultDataRoamingEnabled();
-            createAllApnList();
-            setDataProfilesAsNeeded();
-            setInitialAttachApn();
-            sortApnContextByPriority();
-            cleanUpConnectionsOnUpdatedApns(true, Phone.REASON_CARRIER_CHANGE);
-            setupDataOnAllConnectableApns(Phone.REASON_CARRIER_CHANGE, RetryFailures.ALWAYS);
-        } else {
-            log("onCarrierConfigChanged: SIM is not loaded yet.");
-        }
-    }
-
-    private void onSimAbsent() {
-        if (DBG) log("onSimAbsent");
-
-        mConfigReady = false;
-        cleanUpAllConnectionsInternal(true, Phone.REASON_SIM_NOT_READY);
-        mAllApnSettings.clear();
-        mAutoAttachOnCreationConfig = false;
-        // Clear auto attach as modem is expected to do a new attach once SIM is ready
-        mAutoAttachEnabled.set(false);
-        // In no-sim case, we should still send the emergency APN to the modem, if there is any.
-        createAllApnList();
-        setDataProfilesAsNeeded();
-    }
-
-    private void onSimStateUpdated(@SimState int simState) {
-        mSimState = simState;
-
-        if (DBG) {
-            log("onSimStateUpdated: state=" + SubscriptionInfoUpdater.simStateString(mSimState));
-        }
-
-        if (mSimState == TelephonyManager.SIM_STATE_ABSENT) {
-            onSimAbsent();
-        } else if (mSimState == TelephonyManager.SIM_STATE_LOADED) {
-            mDataThrottler.reset();
-            if (mConfigReady) {
-                createAllApnList();
-                setDataProfilesAsNeeded();
-                setInitialAttachApn();
-                setupDataOnAllConnectableApns(Phone.REASON_SIM_LOADED, RetryFailures.ALWAYS);
-            } else {
-                log("onSimStateUpdated: config not ready yet.");
-            }
-        }
-    }
-
-    private void onApnUnthrottled(String apn) {
-        if (apn != null) {
-            ApnSetting apnSetting = mAllApnSettings.stream()
-                    .filter(as -> apn.equals(as.getApnName()))
-                    .findFirst()
-                    .orElse(null);
-            if (apnSetting != null) {
-                @ApnType int apnTypes = apnSetting.getApnTypeBitmask();
-                mDataThrottler.setRetryTime(apnTypes, RetryManager.NO_SUGGESTED_RETRY_DELAY,
-                        REQUEST_TYPE_NORMAL);
-            } else {
-                loge("EVENT_APN_UNTHROTTLED: Invalid APN passed: " + apn);
-            }
-        } else {
-            loge("EVENT_APN_UNTHROTTLED: apn is null");
-        }
-    }
-
-    private void onTrafficDescriptorsUpdated() {
-        for (ApnContext apnContext : mPrioritySortedApnContexts) {
-            if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
-                    && apnContext.getApnSetting().getPermanentFailed()) {
-                setupDataOnConnectableApn(
-                        apnContext, Phone.REASON_TRAFFIC_DESCRIPTORS_UPDATED, RetryFailures.ALWAYS);
-            }
-        }
-    }
-
-    private DataConnection checkForCompatibleDataConnection(ApnContext apnContext,
-            ApnSetting nextApn) {
-        int apnType = apnContext.getApnTypeBitmask();
-        ArrayList<ApnSetting> dunSettings = null;
-
-        if (ApnSetting.TYPE_DUN == apnType) {
-            dunSettings = fetchDunApns();
-        }
-        if (DBG) {
-            log("checkForCompatibleDataConnection: apnContext=" + apnContext);
-        }
-
-        DataConnection potentialDc = null;
-        for (DataConnection curDc : mDataConnections.values()) {
-            if (curDc != null) {
-                ApnSetting apnSetting = curDc.getApnSetting();
-                log("apnSetting: " + apnSetting);
-                if (dunSettings != null && dunSettings.size() > 0) {
-                    for (ApnSetting dunSetting : dunSettings) {
-                        //This ignore network type as a check which is ok because that's checked
-                        //when calculating dun candidates.
-                        if (areCompatible(dunSetting, apnSetting)) {
-                            if (curDc.isActive()) {
-                                if (DBG) {
-                                    log("checkForCompatibleDataConnection:"
-                                            + " found dun conn=" + curDc);
-                                }
-                                return curDc;
-                            } else if (curDc.isActivating()) {
-                                potentialDc = curDc;
-                            }
-                        }
-                    }
-                } else if (isApnSettingCompatible(curDc, apnType)) {
-                    if (curDc.isActive()) {
-                        if (DBG) {
-                            log("checkForCompatibleDataConnection:"
-                                    + " found canHandle conn=" + curDc);
-                        }
-                        return curDc;
-                    } else if (curDc.isActivating()
-                            || (apnSetting !=  null && apnSetting.equals(nextApn))) {
-                        potentialDc = curDc;
-                    }
-                }
-            }
-        }
-
-        if (DBG) {
-            log("checkForCompatibleDataConnection: potential dc=" + potentialDc);
-        }
-        return potentialDc;
-    }
-
-    private boolean isApnSettingCompatible(DataConnection dc, int apnType) {
-        ApnSetting apnSetting = dc.getApnSetting();
-        if (apnSetting == null) return false;
-
-        // Nothing can be compatible with type ENTERPRISE
-        for (ApnContext apnContext : dc.getApnContexts()) {
-            if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-                return false;
-            }
-        }
-
-        return apnSetting.canHandleType(apnType);
-    }
-
-    private void addHandoverCompleteMsg(Message onCompleteMsg,
-            @ApnType int apnType) {
-        if (onCompleteMsg != null) {
-            List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
-            if (messageList == null) messageList = new ArrayList<>();
-            messageList.add(onCompleteMsg);
-            mHandoverCompletionMsgs.put(apnType, messageList);
-        }
-    }
-
-    private void sendHandoverCompleteMessages(@ApnType int apnType, boolean success,
-            boolean fallbackOnFailedHandover) {
-        List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
-        if (messageList != null) {
-            for (Message msg : messageList) {
-                sendHandoverCompleteMsg(msg, success, mTransportType, fallbackOnFailedHandover);
-            }
-            messageList.clear();
-        }
-    }
-
-    private void sendHandoverCompleteMsg(Message message, boolean success,
-            @TransportType int transport, boolean doFallbackOnFailedHandover) {
-        if (message == null) return;
-
-        Bundle b = message.getData();
-        b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
-        b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
-        b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover);
-        message.sendToTarget();
-    }
-
-    private static boolean shouldFallbackOnFailedHandover(
-                               @HandoverFailureMode int handoverFailureMode,
-                               @RequestNetworkType int requestType,
-                               @DataFailureCause int cause) {
-        if (requestType != REQUEST_TYPE_HANDOVER) {
-            //The fallback is only relevant if the request is a handover
-            return false;
-        } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_DO_FALLBACK) {
-            return true;
-        } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_LEGACY) {
-            return cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Calculates the new request type that will be used the next time a data connection retries
-     * after a failed data call attempt.
-     */
-    @RequestNetworkType
-    public static int calculateNewRetryRequestType(@HandoverFailureMode int handoverFailureMode,
-            @RequestNetworkType int requestType,
-            @DataFailureCause int cause) {
-        boolean fallbackOnFailedHandover =
-                shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause);
-        if (requestType != REQUEST_TYPE_HANDOVER) {
-            //The fallback is only relevant if the request is a handover
-            return requestType;
-        }
-
-        if (fallbackOnFailedHandover) {
-            // Since fallback is happening, the request type is really "NONE".
-            return REQUEST_TYPE_NORMAL;
-        }
-
-        if (handoverFailureMode == HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL) {
-            return REQUEST_TYPE_NORMAL;
-        }
-
-        return REQUEST_TYPE_HANDOVER;
-    }
-
-    public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType,
-            Message onHandoverCompleteMsg) {
-        sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType,
-                onHandoverCompleteMsg));
-    }
-
-    private void onEnableApn(@ApnType int apnType, @RequestNetworkType int requestType,
-            Message onHandoverCompleteMsg) {
-        ApnContext apnContext = mApnContextsByType.get(apnType);
-        if (apnContext == null) {
-            loge("onEnableApn(" + apnType + "): NO ApnContext");
-            if (onHandoverCompleteMsg != null) {
-                sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
-            }
-            return;
-        }
-
-        String str = "onEnableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
-                + ", request type=" + requestTypeToString(requestType);
-        if (DBG) log(str);
-        ApnContext.requestLog(apnContext, str);
-
-        if (!apnContext.isDependencyMet()) {
-            apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
-            apnContext.setEnabled(true);
-            str = "onEnableApn: dependency is not met.";
-            if (DBG) log(str);
-            ApnContext.requestLog(apnContext, str);
-            if (onHandoverCompleteMsg != null) {
-                sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
-            }
-            return;
-        }
-
-        if (apnContext.isReady()) {
-            DctConstants.State state = apnContext.getState();
-            switch(state) {
-                case CONNECTING:
-                    if (onHandoverCompleteMsg != null) {
-                        if (DBG) {
-                            log("onEnableApn: already in CONNECTING state. Handover request "
-                                    + "will be responded after connected.");
-                        }
-                        addHandoverCompleteMsg(onHandoverCompleteMsg, apnType);
-                    } else {
-                        if (DBG) log("onEnableApn: in CONNECTING state. Exit now.");
-                    }
-                    return;
-                case CONNECTED:
-                    if (onHandoverCompleteMsg != null) {
-                        sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType,
-                                false);
-                        if (DBG) {
-                            log("onEnableApn: already in CONNECTED state. Consider as handover "
-                                    + "succeeded");
-                        }
-                    } else {
-                        if (DBG) log("onEnableApn: APN in CONNECTED state. Exit now.");
-                    }
-                    return;
-                case IDLE:
-                case FAILED:
-                case RETRYING:
-                    // We're "READY" but not active so disconnect (cleanup = true) and
-                    // connect (trySetup = true) to be sure we retry the connection.
-                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
-                    break;
-            }
-        } else {
-            if (apnContext.isEnabled()) {
-                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
-            } else {
-                apnContext.setReason(Phone.REASON_DATA_ENABLED);
-            }
-            if (apnContext.getState() == DctConstants.State.FAILED) {
-                apnContext.setState(DctConstants.State.IDLE);
-            }
-        }
-        apnContext.setEnabled(true);
-        apnContext.resetErrorCodeRetries();
-
-        if (mConfigReady || apnContext.getApnTypeBitmask() == ApnSetting.TYPE_EMERGENCY) {
-            trySetupData(apnContext, requestType, onHandoverCompleteMsg);
-        } else {
-            log("onEnableApn: config not ready yet.");
-        }
-    }
-
-    public void disableApn(@ApnType int apnType, @ReleaseNetworkType int releaseType) {
-        sendMessage(obtainMessage(DctConstants.EVENT_DISABLE_APN, apnType, releaseType));
-    }
-
-    private void onDisableApn(@ApnType int apnType,
-                              @ReleaseNetworkType int releaseType) {
-        ApnContext apnContext = mApnContextsByType.get(apnType);
-        if (apnContext == null) {
-            loge("disableApn(" + apnType + "): NO ApnContext");
-            return;
-        }
-
-        boolean cleanup = false;
-        String str = "onDisableApn: apnType=" + ApnSetting.getApnTypeString(apnType)
-                + ", release type=" + releaseTypeToString(releaseType);
-        if (DBG) log(str);
-        ApnContext.requestLog(apnContext, str);
-
-        if (apnContext.isReady()) {
-            cleanup = (releaseType == RELEASE_TYPE_DETACH
-                    || releaseType == RELEASE_TYPE_HANDOVER);
-            if (apnContext.isDependencyMet()) {
-                apnContext.setReason(Phone.REASON_DATA_DISABLED_INTERNAL);
-                // If ConnectivityService has disabled this network, stop trying to bring
-                // it up, but do not tear it down - ConnectivityService will do that
-                // directly by talking with the DataConnection.
-                //
-                // This doesn't apply to DUN. When the user disable tethering, we would like to
-                // detach the APN context from the data connection so the data connection can be
-                // torn down if no other APN context attached to it.
-                if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
-                        || apnContext.getState() != DctConstants.State.CONNECTED) {
-                    str = "Clean up the connection. Apn type = " + apnContext.getApnType()
-                            + ", state = " + apnContext.getState();
-                    if (DBG) log(str);
-                    ApnContext.requestLog(apnContext, str);
-                    cleanup = true;
-                }
-            } else {
-                apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
-            }
-        }
-
-        apnContext.setEnabled(false);
-        if (cleanup) {
-            cleanUpConnectionInternal(true, releaseType, apnContext);
-        }
-
-        if (isOnlySingleDcAllowed(getDataRat()) && !isHigherPriorityApnContextActive(apnContext)) {
-            if (DBG) log("disableApn:isOnlySingleDcAllowed true & higher priority APN disabled");
-            // If the highest priority APN is disabled and only single
-            // data call is allowed, try to setup data call on other connectable APN.
-            setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION,
-                    RetryFailures.ALWAYS);
-        }
-    }
-
-    /**
-     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value for user modification only
-     */
-    public void setDataRoamingEnabledByUser(boolean enabled) {
-        mDataEnabledSettings.setDataRoamingEnabled(enabled);
-        setDataRoamingFromUserAction(true);
-        if (DBG) {
-            log("setDataRoamingEnabledByUser: set phoneSubId=" + mPhone.getSubId()
-                    + " isRoaming=" + enabled);
-        }
-    }
-
-    /**
-     * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
-     */
-    public boolean getDataRoamingEnabled() {
-        boolean isDataRoamingEnabled = mDataEnabledSettings.getDataRoamingEnabled();
-
-        if (VDBG) {
-            log("getDataRoamingEnabled: phoneSubId=" + mPhone.getSubId()
-                    + " isDataRoamingEnabled=" + isDataRoamingEnabled);
-        }
-        return isDataRoamingEnabled;
-    }
-
-    /**
-     * Set default value for {@link android.provider.Settings.Global#DATA_ROAMING}
-     * if the setting is not from user actions. default value is based on carrier config and system
-     * properties.
-     */
-    private void setDefaultDataRoamingEnabled() {
-        // For single SIM phones, this is a per phone property.
-        String setting = Settings.Global.DATA_ROAMING;
-        boolean useCarrierSpecificDefault = false;
-        if (mTelephonyManager.getSimCount() != 1) {
-            setting = setting + mPhone.getSubId();
-            try {
-                Settings.Global.getInt(mResolver, setting);
-            } catch (SettingNotFoundException ex) {
-                // For msim, update to carrier default if uninitialized.
-                useCarrierSpecificDefault = true;
-            }
-        } else if (!isDataRoamingFromUserAction()) {
-            // for single sim device, update to carrier default if user action is not set
-            useCarrierSpecificDefault = true;
-        }
-        log("setDefaultDataRoamingEnabled: useCarrierSpecificDefault "
-                + useCarrierSpecificDefault);
-        if (useCarrierSpecificDefault) {
-            boolean defaultVal = mDataEnabledSettings.getDefaultDataRoamingEnabled();
-            mDataEnabledSettings.setDataRoamingEnabled(defaultVal);
-        }
-    }
-
-    private boolean isDataRoamingFromUserAction() {
-        final SharedPreferences sp = PreferenceManager
-                .getDefaultSharedPreferences(mPhone.getContext());
-        // since we don't want to unset user preference from system update, pass true as the default
-        // value if shared pref does not exist and set shared pref to false explicitly from factory
-        // reset.
-        if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
-            sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
-        }
-        return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
-    }
-
-    private void setDataRoamingFromUserAction(boolean isUserAction) {
-        final SharedPreferences.Editor sp = PreferenceManager
-                .getDefaultSharedPreferences(mPhone.getContext()).edit();
-        sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, isUserAction).commit();
-    }
-
-    // When the data roaming status changes from roaming to non-roaming.
-    private void onDataRoamingOff() {
-        if (DBG) log("onDataRoamingOff");
-
-        reevaluateDataConnections();
-
-        if (!getDataRoamingEnabled()) {
-            // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn
-            // attach and send the data profile again as the modem should have both roaming and
-            // non-roaming protocol in place. Modem should choose the right protocol based on the
-            // roaming condition.
-            setDataProfilesAsNeeded();
-            setInitialAttachApn();
-
-            // If the user did not enable data roaming, now when we transit from roaming to
-            // non-roaming, we should try to reestablish the data connection.
-
-            setupDataOnAllConnectableApns(Phone.REASON_ROAMING_OFF, RetryFailures.ALWAYS);
-        }
-    }
-
-    // This method is called
-    // 1. When the data roaming status changes from non-roaming to roaming.
-    // 2. When allowed data roaming settings is changed by the user.
-    private void onDataRoamingOnOrSettingsChanged(int messageType) {
-        if (DBG) log("onDataRoamingOnOrSettingsChanged");
-        // Used to differentiate data roaming turned on vs settings changed.
-        boolean settingChanged = (messageType == DctConstants.EVENT_ROAMING_SETTING_CHANGE);
-
-        // Check if the device is actually data roaming
-        if (!mPhone.getServiceState().getDataRoaming()) {
-            if (DBG) log("device is not roaming. ignored the request.");
-            return;
-        }
-
-        checkDataRoamingStatus(settingChanged);
-
-        if (getDataRoamingEnabled()) {
-            // If the restricted data was brought up when data roaming is disabled, and now users
-            // enable data roaming, we need to re-evaluate the conditions and possibly change the
-            // network's capability.
-            if (settingChanged) {
-                reevaluateDataConnections();
-            }
-
-            if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
-
-            setupDataOnAllConnectableApns(Phone.REASON_ROAMING_ON, RetryFailures.ALWAYS);
-        } else {
-            // If the user does not turn on data roaming, when we transit from non-roaming to
-            // roaming, we need to tear down the data connection otherwise the user might be
-            // charged for data roaming usage.
-            if (DBG) log("onDataRoamingOnOrSettingsChanged: Tear down data connection on roaming.");
-            cleanUpAllConnectionsInternal(true, Phone.REASON_ROAMING_ON);
-        }
-    }
-
-    // We want to track possible roaming data leakage. Which is, if roaming setting
-    // is disabled, yet we still setup a roaming data connection or have a connected ApnContext
-    // switched to roaming. When this happens, we log it in a local log.
-    private void checkDataRoamingStatus(boolean settingChanged) {
-        if (!settingChanged && !getDataRoamingEnabled()
-                && mPhone.getServiceState().getDataRoaming()) {
-            for (ApnContext apnContext : mApnContexts.values()) {
-                if (apnContext.getState() == DctConstants.State.CONNECTED) {
-                    mDataRoamingLeakageLog.log("PossibleRoamingLeakage "
-                            + " connection params: " + (apnContext.getDataConnection() != null
-                            ? apnContext.getDataConnection().getConnectionParams() : ""));
-                }
-            }
-        }
-    }
-
-    private void onRadioAvailable() {
-        if (DBG) log("onRadioAvailable");
-        if (!areAllDataDisconnected()) {
-            cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, null);
-        }
-    }
-
-    private void onRadioOffOrNotAvailable() {
-        // Make sure our reconnect delay starts at the initial value
-        // next time the radio comes on
-
-        mReregisterOnReconnectFailure = false;
-
-        // Clear auto attach as modem is expected to do a new attach
-        mAutoAttachEnabled.set(false);
-
-        if (mPhone.getSimulatedRadioControl() != null) {
-            // Assume data is connected on the simulator
-            // FIXME  this can be improved
-            log("We're on the simulator; assuming radio off is meaningless");
-        } else {
-            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
-            cleanUpAllConnectionsInternal(false, Phone.REASON_RADIO_TURNED_OFF);
-        }
-    }
-
-    private void completeConnection(ApnContext apnContext, @RequestNetworkType int type) {
-
-        if (DBG) log("completeConnection: successful, notify the world apnContext=" + apnContext);
-
-        if (mIsProvisioning && !TextUtils.isEmpty(mProvisioningUrl)) {
-            if (DBG) {
-                log("completeConnection: MOBILE_PROVISIONING_ACTION url="
-                        + mProvisioningUrl);
-            }
-            Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
-                    Intent.CATEGORY_APP_BROWSER);
-            newIntent.setData(Uri.parse(mProvisioningUrl));
-            newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                    Intent.FLAG_ACTIVITY_NEW_TASK);
-            try {
-                mPhone.getContext().startActivity(newIntent);
-            } catch (ActivityNotFoundException e) {
-                loge("completeConnection: startActivityAsUser failed" + e);
-            }
-        }
-        mIsProvisioning = false;
-        mProvisioningUrl = null;
-        if (mProvisioningSpinner != null) {
-            sendMessage(obtainMessage(DctConstants.CMD_CLEAR_PROVISIONING_SPINNER,
-                    mProvisioningSpinner));
-        }
-
-        startNetStatPoll();
-        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-
-        PersistableBundle b = getCarrierConfig();
-        if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
-                && b.getBoolean(CarrierConfigManager
-                .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
-            NotificationManager notificationManager = (NotificationManager)
-                    mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
-            notificationManager.cancel(Integer.toString(mPhone.getSubId()),
-                    NO_DATA_NOTIFICATION);
-        }
-    }
-
-    /**
-     * A SETUP (aka bringUp) has completed, possibly with an error. If
-     * there is an error this method will call {@link #onDataSetupCompleteError}.
-     */
-    protected void onDataSetupComplete(ApnContext apnContext, boolean success,
-            @DataFailureCause int cause, @RequestNetworkType int requestType,
-            @HandoverFailureMode int handoverFailureMode) {
-        boolean fallbackOnFailedHandover = shouldFallbackOnFailedHandover(
-                handoverFailureMode, requestType, cause);
-
-        if (success && (handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN
-                && handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)) {
-            Log.wtf(mLogTag, "bad failure mode: "
-                    + DataCallResponse.failureModeToString(handoverFailureMode));
-        } else if (handoverFailureMode
-                != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER
-                && cause != DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE) {
-            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), success,
-                    fallbackOnFailedHandover);
-        }
-
-        if (success) {
-            DataConnection dataConnection = apnContext.getDataConnection();
-
-            if (RADIO_TESTS) {
-                // Note: To change radio.test.onDSC.null.dcac from command line you need to
-                // adb root and adb remount and from the command line you can only change the
-                // value to 1 once. To change it a second time you can reboot or execute
-                // adb shell stop and then adb shell start. The command line to set the value is:
-                // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
-                ContentResolver cr = mPhone.getContext().getContentResolver();
-                String radioTestProperty = "radio.test.onDSC.null.dcac";
-                if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
-                    log("onDataSetupComplete: " + radioTestProperty +
-                            " is true, set dcac to null and reset property to false");
-                    dataConnection = null;
-                    Settings.System.putInt(cr, radioTestProperty, 0);
-                    log("onDataSetupComplete: " + radioTestProperty + "=" +
-                            Settings.System.getInt(mPhone.getContext().getContentResolver(),
-                                    radioTestProperty, -1));
-                }
-            }
-            if (dataConnection == null) {
-                log("onDataSetupComplete: no connection to DC, handle as error");
-                onDataSetupCompleteError(apnContext, requestType, false);
-            } else {
-                ApnSetting apn = apnContext.getApnSetting();
-                if (DBG) {
-                    log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
-                            : apn.getApnName()));
-                }
-
-                // everything is setup
-                if (TextUtils.equals(apnContext.getApnType(), ApnSetting.TYPE_DEFAULT_STRING)
-                        && mCanSetPreferApn && mPreferredApn == null) {
-                    if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
-                    mPreferredApn = apn;
-                    if (mPreferredApn != null) {
-                        setPreferredApn(mPreferredApn.getId());
-                    }
-                }
-
-                // A connection is setup
-                apnContext.setState(DctConstants.State.CONNECTED);
-
-                checkDataRoamingStatus(false);
-
-                boolean isProvApn = apnContext.isProvisioningApn();
-                final ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
-                        .getSystemService(Context.CONNECTIVITY_SERVICE);
-                if (mProvisionBroadcastReceiver != null) {
-                    mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
-                    mProvisionBroadcastReceiver = null;
-                }
-
-                if ((!isProvApn) || mIsProvisioning) {
-                    if (mIsProvisioning) {
-                        // Hide any notification that was showing previously
-                        hideProvisioningNotification();
-                    }
-
-                    // Complete the connection normally notifying the world we're connected.
-                    // We do this if this isn't a special provisioning apn or if we've been
-                    // told its time to provision.
-                    completeConnection(apnContext, requestType);
-                } else {
-                    // This is a provisioning APN that we're reporting as connected. Later
-                    // when the user desires to upgrade this to a "default" connection,
-                    // mIsProvisioning == true, we'll go through the code path above.
-                    // mIsProvisioning becomes true when CMD_ENABLE_MOBILE_PROVISIONING
-                    // is sent to the DCT.
-                    if (DBG) {
-                        log("onDataSetupComplete: successful, BUT send connected to prov apn as"
-                                + " mIsProvisioning:" + mIsProvisioning + " == false"
-                                + " && (isProvisioningApn:" + isProvApn + " == true");
-                    }
-
-                    // While radio is up, grab provisioning URL.  The URL contains ICCID which
-                    // disappears when radio is off.
-                    mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
-                            mPhone.getMobileProvisioningUrl(),
-                            mTelephonyManager.getNetworkOperatorName());
-                    mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
-                            new IntentFilter(INTENT_PROVISION));
-
-                    // Put up user notification that sign-in is required.
-                    showProvisioningNotification();
-
-                    // Turn off radio to save battery and avoid wasting carrier resources.
-                    // The network isn't usable and network validation will just fail anyhow.
-                    setRadio(false);
-                }
-                if (DBG) {
-                    log("onDataSetupComplete: SETUP complete type=" + apnContext.getApnType());
-                }
-                if (TelephonyUtils.IS_DEBUGGABLE) {
-                    // adb shell setprop persist.radio.test.pco [pco_val]
-                    String radioTestProperty = "persist.radio.test.pco";
-                    int pcoVal = SystemProperties.getInt(radioTestProperty, -1);
-                    if (pcoVal != -1) {
-                        log("PCO testing: read pco value from persist.radio.test.pco " + pcoVal);
-                        final byte[] value = new byte[1];
-                        value[0] = (byte) pcoVal;
-                        final Intent intent =
-                                new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
-                        intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_DEFAULT);
-                        intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
-                                ApnSetting.PROTOCOL_IPV4V6);
-                        intent.putExtra(TelephonyManager.EXTRA_PCO_ID, 0xFF00);
-                        intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, value);
-                        mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
-                    }
-                }
-            }
-        } else {
-            if (DBG) {
-                ApnSetting apn = apnContext.getApnSetting();
-                log("onDataSetupComplete: error apn=" + apn.getApnName() + ", cause="
-                        + DataFailCause.toString(cause) + ", requestType="
-                        + requestTypeToString(requestType));
-            }
-            if (DataFailCause.isEventLoggable(cause)) {
-                // Log this failure to the Event Logs.
-                int cid = getCellLocationId();
-                EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
-                        cause, cid, mTelephonyManager.getNetworkType());
-            }
-            ApnSetting apn = apnContext.getApnSetting();
-
-            // Compose broadcast intent send to the specific carrier signaling receivers
-            Intent intent = new Intent(TelephonyManager
-                    .ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
-            intent.putExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, cause);
-            intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
-                    ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType()));
-            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
-
-            if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), cause, mPhone.getSubId())
-                    || apnContext.restartOnError(cause)) {
-                if (DBG) log("Modem restarted.");
-                sendRestartRadio();
-            }
-
-            // If the data call failure cause is a permanent failure, we mark the APN as permanent
-            // failed.
-            if (isPermanentFailure(cause)) {
-                log("cause=" + DataFailCause.toString(cause)
-                        + ", mark apn as permanent failed. apn = " + apn);
-                apnContext.markApnPermanentFailed(apn);
-
-                PersistableBundle b = getCarrierConfig();
-                if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
-                        && b.getBoolean(CarrierConfigManager
-                        .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
-                    NotificationManager notificationManager = (NotificationManager)
-                            mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
-
-                    CharSequence title = mPhone.getContext().getText(
-                            com.android.internal.R.string.RestrictedOnDataTitle);
-                    CharSequence details = mPhone.getContext().getText(
-                            com.android.internal.R.string.RestrictedStateContent);
-
-                    Notification notification = new Notification.Builder(mPhone.getContext(),
-                            NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
-                            .setWhen(System.currentTimeMillis())
-                            .setAutoCancel(true)
-                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
-                            .setTicker(title)
-                            .setColor(mPhone.getContext().getResources().getColor(
-                                    com.android.internal.R.color.system_notification_accent_color))
-                            .setContentTitle(title)
-                            .setStyle(new Notification.BigTextStyle().bigText(details))
-                            .setContentText(details)
-                            .build();
-                    notificationManager.notify(Integer.toString(mPhone.getSubId()),
-                            NO_DATA_NOTIFICATION, notification);
-                }
-            }
-
-            int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType,
-                    cause);
-            onDataSetupCompleteError(apnContext, newRequestType, fallbackOnFailedHandover);
-        }
-    }
-
-
-
-    /**
-     * Error has occurred during the SETUP {aka bringUP} request and the DCT
-     * should either try the next waiting APN or start over from the
-     * beginning if the list is empty. Between each SETUP request there will
-     * be a delay defined by {@link ApnContext#getDelayForNextApn(boolean)}.
-     */
-    protected void onDataSetupCompleteError(ApnContext apnContext,
-            @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) {
-        long delay = apnContext.getDelayForNextApn(mFailFast);
-        // Check if we need to retry or not.
-        if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallbackOnFailedHandover) {
-            if (DBG) {
-                log("onDataSetupCompleteError: APN type=" + apnContext.getApnType()
-                        + ". Request type=" + requestTypeToString(requestType) + ", Retry in "
-                        + delay + "ms.");
-            }
-            startReconnect(delay, apnContext, requestType);
-        } else {
-            // If we are not going to retry any APN, set this APN context to failed state.
-            // This would be the final state of a data connection.
-            apnContext.setState(DctConstants.State.FAILED);
-            apnContext.setDataConnection(null);
-            log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
-                    + ", requestType=" + requestTypeToString(requestType));
-            //send request network complete messages as needed
-            sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
-                    fallbackOnFailedHandover);
-        }
-    }
-
-    /**
-     * Called when EVENT_NETWORK_STATUS_CHANGED is received.
-     *
-     * @param status One of {@code NetworkAgent.VALID_NETWORK} or
-     * {@code NetworkAgent.INVALID_NETWORK}.
-     * @param cid context id {@code cid}
-     * @param redirectUrl If the Internet probe was redirected, this
-     * is the destination it was redirected to, otherwise {@code null}
-     */
-    private void onNetworkStatusChanged(int status, int cid, String redirectUrl) {
-        if (!TextUtils.isEmpty(redirectUrl)) {
-            Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED);
-            intent.putExtra(TelephonyManager.EXTRA_REDIRECTION_URL, redirectUrl);
-            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
-            log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
-        } else {
-            final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID;
-            final DataConnection dc = getDataConnectionByContextId(cid);
-            if (!mDsRecoveryHandler.isRecoveryOnBadNetworkEnabled()) {
-                if (DBG) log("Skip data stall recovery on network status change with in threshold");
-                return;
-            }
-            if (mTransportType != AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                if (DBG) log("Skip data stall recovery on non WWAN");
-                return;
-            }
-            if (dc != null && dc.isValidationRequired()) {
-                mDsRecoveryHandler.processNetworkStatusChanged(isValid);
-            }
-        }
-    }
-
-    /**
-     * Called when EVENT_DISCONNECT_DONE is received.
-     */
-    private void onDisconnectDone(ApnContext apnContext) {
-        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
-        apnContext.setState(DctConstants.State.IDLE);
-        // If all data connection are gone, check whether Airplane mode request was pending.
-        if (areAllDataDisconnected()
-                && mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
-            if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
-            // Radio will be turned off. No need to retry data setup
-            apnContext.setApnSetting(null);
-            apnContext.setDataConnection(null);
-
-            // Need to notify disconnect as well, in the case of switching Airplane mode.
-            // Otherwise, it would cause 30s delayed to turn on Airplane mode.
-            notifyAllDataDisconnected();
-            return;
-        }
-        // If APN is still enabled, try to bring it back up automatically
-        if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
-            // Wait a bit before trying the next APN, so that
-            // we're not tying up the RIL command channel.
-            // This also helps in any external dependency to turn off the context.
-            if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
-
-            // See if there are still handover request pending that we need to retry handover
-            // after previous data gets disconnected.
-            if (isHandoverPending(apnContext.getApnTypeBitmask())) {
-                if (DBG) log("Handover request pending. Retry handover immediately.");
-                startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER);
-            } else {
-                long delay = apnContext.getRetryAfterDisconnectDelay();
-                if (delay > 0) {
-                    // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
-                    // the waiting APN list, which will also reset/reconfigure the retry manager.
-                    startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL);
-                }
-            }
-        } else {
-            boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
-                    com.android.internal.R.bool.config_restartRadioAfterProvisioning);
-
-            if (apnContext.isProvisioningApn() && restartRadioAfterProvisioning) {
-                log("onDisconnectDone: restartRadio after provisioning");
-                restartRadio();
-            }
-            apnContext.setApnSetting(null);
-            apnContext.setDataConnection(null);
-            if (isOnlySingleDcAllowed(getDataRat())) {
-                if(DBG) log("onDisconnectDone: isOnlySigneDcAllowed true so setup single apn");
-                setupDataOnAllConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION,
-                        RetryFailures.ALWAYS);
-            } else {
-                if(DBG) log("onDisconnectDone: not retrying");
-            }
-        }
-
-        if (areAllDataDisconnected()) {
-            apnContext.setConcurrentVoiceAndDataAllowed(
-                    mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed());
-            notifyAllDataDisconnected();
-        }
-
-    }
-
-    private void onVoiceCallStarted() {
-        if (DBG) log("onVoiceCallStarted");
-        mInVoiceCall = true;
-        if (isAnyDataConnected()
-                && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-            if (DBG) log("onVoiceCallStarted stop polling");
-            stopNetStatPoll();
-            stopDataStallAlarm();
-        }
-    }
-
-    protected void onVoiceCallEnded() {
-        if (DBG) log("onVoiceCallEnded");
-        mInVoiceCall = false;
-        if (isAnyDataConnected()) {
-            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-                startNetStatPoll();
-                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-            } else {
-                // clean slate after call end.
-                resetPollStats();
-            }
-        }
-        // reset reconnect timer
-        setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS);
-    }
-    /**
-     * @return {@code true} if there is any data in connected state.
-     */
-    @VisibleForTesting
-    public boolean isAnyDataConnected() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (dc.isActive()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @return {@code true} if all data connections are in disconnected state.
-     */
-    public boolean areAllDataDisconnected() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (!dc.isInactive()) {
-                if (DBG) log("areAllDataDisconnected false due to DC: " + dc.getName());
-                return false;
-            }
-        }
-        return true;
-    }
-
-    protected void setDataProfilesAsNeeded() {
-        if (DBG) log("setDataProfilesAsNeeded");
-
-        ArrayList<DataProfile> dataProfileList = new ArrayList<>();
-
-        int preferredApnSetId = getPreferredApnSetId();
-        for (ApnSetting apn : mAllApnSettings) {
-            if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
-                    || preferredApnSetId == apn.getApnSetId()) {
-                DataProfile dp = new DataProfile.Builder()
-                        .setApnSetting(apn)
-                        .setPreferred(apn.equals(getPreferredApn()))
-                        .build();
-                if (!dataProfileList.contains(dp)) {
-                    dataProfileList.add(dp);
-                }
-            } else {
-                if (VDBG) {
-                    log("setDataProfilesAsNeeded: APN set id " + apn.getApnSetId()
-                            + " does not match the preferred set id " + preferredApnSetId);
-                }
-            }
-        }
-
-        // Check if the data profiles we are sending are same as we did last time. We don't want to
-        // send the redundant profiles to the modem. Also if there the list is empty, we don't
-        // send it to the modem.
-        if (!dataProfileList.isEmpty()
-                && (dataProfileList.size() != mLastDataProfileList.size()
-                || !mLastDataProfileList.containsAll(dataProfileList))) {
-            mDataServiceManager.setDataProfile(dataProfileList,
-                    mPhone.getServiceState().getDataRoamingFromRegistration(), null);
-        }
-    }
-
-    /**
-     * Based on the sim operator numeric, create a list for all possible
-     * Data Connections and setup the preferredApn.
-     */
-    protected void createAllApnList() {
-        mAllApnSettings.clear();
-        String operator = mPhone.getOperatorNumeric();
-
-        // ORDER BY Telephony.Carriers._ID ("_id")
-        Cursor cursor = mPhone.getContext().getContentResolver().query(
-                Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, "filtered/subId/"
-                        + mPhone.getSubId()), null, null, null, Telephony.Carriers._ID);
-
-        if (cursor != null) {
-            while (cursor.moveToNext()) {
-                ApnSetting apn = ApnSetting.makeApnSetting(cursor);
-                if (apn == null) {
-                    continue;
-                }
-                mAllApnSettings.add(apn);
-            }
-            cursor.close();
-        } else {
-            if (DBG) log("createAllApnList: cursor is null");
-            mApnSettingsInitializationLog.log("cursor is null for carrier, operator: "
-                    + operator);
-        }
-
-        dedupeApnSettings();
-
-        if (mAllApnSettings.isEmpty()) {
-            log("createAllApnList: No APN found for carrier, operator: " + operator);
-            mApnSettingsInitializationLog.log("no APN found for carrier, operator: "
-                    + operator);
-            mPreferredApn = null;
-        } else {
-            mPreferredApn = getPreferredApn();
-            if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
-        }
-
-        addDefaultApnSettingsAsNeeded();
-        if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
-    }
-
-    private void dedupeApnSettings() {
-        ArrayList<ApnSetting> resultApns = new ArrayList<ApnSetting>();
-
-        // coalesce APNs if they are similar enough to prevent
-        // us from bringing up two data calls with the same interface
-        int i = 0;
-        while (i < mAllApnSettings.size() - 1) {
-            ApnSetting first = mAllApnSettings.get(i);
-            ApnSetting second = null;
-            int j = i + 1;
-            while (j < mAllApnSettings.size()) {
-                second = mAllApnSettings.get(j);
-                if (first.similar(second)) {
-                    ApnSetting newApn = mergeApns(first, second);
-                    mAllApnSettings.set(i, newApn);
-                    first = newApn;
-                    mAllApnSettings.remove(j);
-                } else {
-                    j++;
-                }
-            }
-            i++;
-        }
-    }
-
-    private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
-        int id = dest.getId();
-        if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
-            id = src.getId();
-        }
-        final int resultApnType = src.getApnTypeBitmask() | dest.getApnTypeBitmask();
-        Uri mmsc = (dest.getMmsc() == null ? src.getMmsc() : dest.getMmsc());
-        String mmsProxy = TextUtils.isEmpty(dest.getMmsProxyAddressAsString())
-                ? src.getMmsProxyAddressAsString() : dest.getMmsProxyAddressAsString();
-        int mmsPort = dest.getMmsProxyPort() == -1 ? src.getMmsProxyPort() : dest.getMmsProxyPort();
-        String proxy = TextUtils.isEmpty(dest.getProxyAddressAsString())
-                ? src.getProxyAddressAsString() : dest.getProxyAddressAsString();
-        int port = dest.getProxyPort() == -1 ? src.getProxyPort() : dest.getProxyPort();
-        int protocol = src.getProtocol() == ApnSetting.PROTOCOL_IPV4V6 ? src.getProtocol()
-                : dest.getProtocol();
-        int roamingProtocol = src.getRoamingProtocol() == ApnSetting.PROTOCOL_IPV4V6
-                ? src.getRoamingProtocol() : dest.getRoamingProtocol();
-        int networkTypeBitmask = (dest.getNetworkTypeBitmask() == 0
-                || src.getNetworkTypeBitmask() == 0)
-                ? 0 : (dest.getNetworkTypeBitmask() | src.getNetworkTypeBitmask());
-        return new ApnSetting.Builder()
-                .setId(id)
-                .setOperatorNumeric(dest.getOperatorNumeric())
-                .setEntryName(dest.getEntryName())
-                .setApnName(dest.getApnName())
-                .setProxyAddress(proxy)
-                .setProxyPort(port)
-                .setMmsc(mmsc)
-                .setMmsProxyAddress(mmsProxy)
-                .setMmsProxyPort(mmsPort)
-                .setUser(dest.getUser())
-                .setPassword(dest.getPassword())
-                .setAuthType(dest.getAuthType())
-                .setApnTypeBitmask(resultApnType)
-                .setProtocol(protocol)
-                .setRoamingProtocol(roamingProtocol)
-                .setCarrierEnabled(dest.isEnabled())
-                .setNetworkTypeBitmask(networkTypeBitmask)
-                .setProfileId(dest.getProfileId())
-                .setModemCognitive(dest.isPersistent() || src.isPersistent())
-                .setMaxConns(dest.getMaxConns())
-                .setWaitTime(dest.getWaitTime())
-                .setMaxConnsTime(dest.getMaxConnsTime())
-                .setMtuV4(dest.getMtuV4())
-                .setMtuV6(dest.getMtuV6())
-                .setMvnoType(dest.getMvnoType())
-                .setMvnoMatchData(dest.getMvnoMatchData())
-                .setApnSetId(dest.getApnSetId())
-                .setCarrierId(dest.getCarrierId())
-                .setSkip464Xlat(dest.getSkip464Xlat())
-                .build();
-    }
-
-    private DataConnection createDataConnection() {
-        if (DBG) log("createDataConnection E");
-
-        int id = mUniqueIdGenerator.getAndIncrement();
-        DataConnection dataConnection = DataConnection.makeDataConnection(mPhone, id, this,
-                mDataServiceManager, mDcTesterFailBringUpAll, mDcc);
-        mDataConnections.put(id, dataConnection);
-        if (DBG) log("createDataConnection() X id=" + id + " dc=" + dataConnection);
-        return dataConnection;
-    }
-
-    private void destroyDataConnections() {
-        if(mDataConnections != null) {
-            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
-            mDataConnections.clear();
-        } else {
-            if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
-        }
-    }
-
-    /**
-     * Build a list of APNs to be used to create PDP's.
-     *
-     * @param requestedApnType
-     * @return waitingApns list to be used to create PDP
-     *          error when waitingApns.isEmpty()
-     */
-    private @NonNull ArrayList<ApnSetting> buildWaitingApns(String requestedApnType,
-            int radioTech) {
-        if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
-        ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
-
-        int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
-        if (requestedApnTypeBitmask == ApnSetting.TYPE_ENTERPRISE) {
-            requestedApnTypeBitmask = ApnSetting.TYPE_DEFAULT;
-        }
-        if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) {
-            ArrayList<ApnSetting> dunApns = fetchDunApns();
-            if (dunApns.size() > 0) {
-                for (ApnSetting dun : dunApns) {
-                    apnList.add(dun);
-                    if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
-                }
-                return apnList;
-            }
-        }
-
-        String operator = mPhone.getOperatorNumeric();
-
-        // This is a workaround for a bug (7305641) where we don't failover to other
-        // suitable APNs if our preferred APN fails.  On prepaid ATT sims we need to
-        // failover to a provisioning APN, but once we've used their default data
-        // connection we are locked to it for life.  This change allows ATT devices
-        // to say they don't want to use preferred at all.
-        boolean usePreferred = true;
-        try {
-            usePreferred = !mPhone.getContext().getResources().getBoolean(com.android
-                    .internal.R.bool.config_dontPreferApn);
-        } catch (Resources.NotFoundException e) {
-            if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
-            usePreferred = true;
-        }
-        if (usePreferred) {
-            mPreferredApn = getPreferredApn();
-        }
-        if (DBG) {
-            log("buildWaitingApns: usePreferred=" + usePreferred
-                    + " canSetPreferApn=" + mCanSetPreferApn
-                    + " mPreferredApn=" + mPreferredApn
-                    + " operator=" + operator + " radioTech=" + radioTech);
-        }
-
-        if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
-                mPreferredApn.canHandleType(requestedApnTypeBitmask)) {
-            if (DBG) {
-                log("buildWaitingApns: Preferred APN:" + operator + ":"
-                        + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn);
-            }
-
-            if (TextUtils.equals(mPreferredApn.getOperatorNumeric(), operator)
-                    || mPreferredApn.getCarrierId() == mPhone.getCarrierId()) {
-                if (mPreferredApn.canSupportNetworkType(
-                        ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
-                    // Create a new instance of ApnSetting for ENTERPRISE because each
-                    // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
-                    // APN as DEFAULT but is a separate DataConnection
-                    if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
-                            == ApnSetting.TYPE_ENTERPRISE) {
-                        apnList.add(ApnSetting.makeApnSetting(mPreferredApn));
-                    } else {
-                        apnList.add(mPreferredApn);
-                    }
-                    if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
-                    return apnList;
-                }
-            }
-            if (DBG) log("buildWaitingApns: no preferred APN");
-            setPreferredApn(-1);
-            mPreferredApn = null;
-        }
-
-        if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
-        int preferredApnSetId = getPreferredApnSetId();
-        for (ApnSetting apn : mAllApnSettings) {
-            if (apn.canHandleType(requestedApnTypeBitmask)) {
-                if (apn.canSupportNetworkType(
-                        ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
-                    if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
-                            || preferredApnSetId == apn.getApnSetId()) {
-                        if (VDBG) log("buildWaitingApns: adding apn=" + apn);
-                        // Create a new instance of ApnSetting for ENTERPRISE because each
-                        // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
-                        // APN as DEFAULT but is a separate DataConnection
-                        if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
-                                == ApnSetting.TYPE_ENTERPRISE) {
-                            apnList.add(ApnSetting.makeApnSetting(apn));
-                        } else {
-                            apnList.add(apn);
-                        }
-                    } else {
-                        log("buildWaitingApns: APN set id " + apn.getApnSetId()
-                                + " does not match the preferred set id " + preferredApnSetId);
-                    }
-                } else {
-                    if (DBG) {
-                        log("buildWaitingApns: networkTypeBitmask:"
-                                + apn.getNetworkTypeBitmask()
-                                + " does not include radioTech:"
-                                + ServiceState.rilRadioTechnologyToString(radioTech));
-                    }
-                }
-            } else if (VDBG) {
-                log("buildWaitingApns: couldn't handle requested ApnType="
-                        + requestedApnType);
-            }
-        }
-
-        if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
-        return apnList;
-    }
-
-    private String apnListToString (ArrayList<ApnSetting> apns) {
-        StringBuilder result = new StringBuilder();
-        for (int i = 0, size = apns.size(); i < size; i++) {
-            result.append('[')
-                  .append(apns.get(i).toString())
-                  .append(']');
-        }
-        return result.toString();
-    }
-
-    private void setPreferredApn(int pos) {
-        setPreferredApn(pos, false);
-    }
-
-    private void setPreferredApn(int pos, boolean force) {
-        if (!force && !mCanSetPreferApn) {
-            log("setPreferredApn: X !canSEtPreferApn");
-            return;
-        }
-
-        String subId = Long.toString(mPhone.getSubId());
-        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
-        log("setPreferredApn: delete");
-        ContentResolver resolver = mPhone.getContext().getContentResolver();
-        resolver.delete(uri, null, null);
-
-        if (pos >= 0) {
-            log("setPreferredApn: insert");
-            ContentValues values = new ContentValues();
-            values.put(APN_ID, pos);
-            resolver.insert(uri, values);
-        }
-    }
-
-    @Nullable
-    ApnSetting getPreferredApn() {
-        //Only call this method from main thread
-        if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
-            log("getPreferredApn: mAllApnSettings is empty");
-            return null;
-        }
-
-        String subId = Long.toString(mPhone.getSubId());
-        Uri uri = Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID, subId);
-        Cursor cursor = mPhone.getContext().getContentResolver().query(
-                uri, new String[] { "_id", "name", "apn" },
-                null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
-
-        if (cursor != null) {
-            mCanSetPreferApn = true;
-        } else {
-            mCanSetPreferApn = false;
-        }
-
-        if (VDBG) {
-            log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
-                    + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
-        }
-
-        if (mCanSetPreferApn && cursor.getCount() > 0) {
-            int pos;
-            cursor.moveToFirst();
-            pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
-            for(ApnSetting p : mAllApnSettings) {
-                if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
-                    log("getPreferredApn: For APN type "
-                            + ApnSetting.getApnTypeString(mRequestedApnType)
-                            + " found apnSetting " + p);
-                    cursor.close();
-                    return p;
-                }
-            }
-        }
-
-        if (cursor != null) {
-            cursor.close();
-        }
-
-        log("getPreferredApn: X not found");
-        return null;
-    }
-
-    @Override
-    public void handleMessage (Message msg) {
-        if (VDBG) log("handleMessage msg=" + msg);
-
-        AsyncResult ar;
-        Pair<ApnContext, Integer> pair;
-        ApnContext apnContext;
-        int generation;
-        int requestType;
-        int handoverFailureMode;
-        switch (msg.what) {
-            case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
-                onDataConnectionDetached();
-                break;
-
-            case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
-                onDataConnectionAttached();
-                break;
-
-            case DctConstants.EVENT_DO_RECOVERY:
-                mDsRecoveryHandler.doRecovery();
-                break;
-
-            case DctConstants.EVENT_APN_CHANGED:
-                onApnChanged();
-                break;
-
-            case DctConstants.EVENT_PS_RESTRICT_ENABLED:
-                /**
-                 * We don't need to explicitly to tear down the PDP context
-                 * when PS restricted is enabled. The base band will deactive
-                 * PDP context and notify us with PDP_CONTEXT_CHANGED.
-                 * But we should stop the network polling and prevent reset PDP.
-                 */
-                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
-                stopNetStatPoll();
-                stopDataStallAlarm();
-                mIsPsRestricted = true;
-                break;
-
-            case DctConstants.EVENT_PS_RESTRICT_DISABLED:
-                /**
-                 * When PS restrict is removed, we need setup PDP connection if
-                 * PDP connection is down.
-                 */
-                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
-                mIsPsRestricted  = false;
-                if (isAnyDataConnected()) {
-                    startNetStatPoll();
-                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-                } else {
-                    // TODO: Should all PDN states be checked to fail?
-                    if (mState == DctConstants.State.FAILED) {
-                        cleanUpAllConnectionsInternal(false, Phone.REASON_PS_RESTRICT_ENABLED);
-                        mReregisterOnReconnectFailure = false;
-                    }
-                    apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
-                    if (apnContext != null) {
-                        apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
-                        trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
-                    } else {
-                        loge("**** Default ApnContext not found ****");
-                        if (TelephonyUtils.IS_DEBUGGABLE) {
-                            throw new RuntimeException("Default ApnContext not found");
-                        }
-                    }
-                }
-                break;
-
-            case DctConstants.EVENT_TRY_SETUP_DATA:
-                apnContext = (ApnContext) msg.obj;
-                requestType = msg.arg1;
-                trySetupData(apnContext, requestType, null);
-                break;
-            case DctConstants.EVENT_CLEAN_UP_CONNECTION:
-                if (DBG) log("EVENT_CLEAN_UP_CONNECTION");
-                cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj);
-                break;
-            case DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS:
-                if ((msg.obj != null) && (msg.obj instanceof String == false)) {
-                    msg.obj = null;
-                }
-                cleanUpAllConnectionsInternal(true, (String) msg.obj);
-                break;
-
-            case DctConstants.EVENT_DATA_RAT_CHANGED:
-                if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
-                    // unknown rat is an exception for data rat change. It's only received when out
-                    // of service and is not applicable for apn bearer bitmask. We should bypass the
-                    // check of waiting apn list and keep the data connection on, and no need to
-                    // setup a new one.
-                    break;
-                }
-                cleanUpConnectionsOnUpdatedApns(false, Phone.REASON_NW_TYPE_CHANGED);
-                //May new Network allow setupData, so try it here
-                setupDataOnAllConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
-                        RetryFailures.ONLY_ON_CHANGE);
-                break;
-
-            case DctConstants.CMD_CLEAR_PROVISIONING_SPINNER:
-                // Check message sender intended to clear the current spinner.
-                if (mProvisioningSpinner == msg.obj) {
-                    mProvisioningSpinner.dismiss();
-                    mProvisioningSpinner = null;
-                }
-                break;
-
-            case DctConstants.EVENT_ENABLE_APN:
-                onEnableApn(msg.arg1, msg.arg2, (Message) msg.obj);
-                break;
-
-            case DctConstants.EVENT_DISABLE_APN:
-                onDisableApn(msg.arg1, msg.arg2);
-                break;
-
-            case DctConstants.EVENT_DATA_STALL_ALARM:
-                onDataStallAlarm(msg.arg1);
-                break;
-
-            case DctConstants.EVENT_ROAMING_OFF:
-                onDataRoamingOff();
-                break;
-
-            case DctConstants.EVENT_ROAMING_ON:
-            case DctConstants.EVENT_ROAMING_SETTING_CHANGE:
-                onDataRoamingOnOrSettingsChanged(msg.what);
-                break;
-
-            case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
-                // Update sharedPreference to false when exits new device provisioning, indicating
-                // no users modifications on the settings for new devices. Thus carrier specific
-                // default roaming settings can be applied for new devices till user modification.
-                final SharedPreferences sp = PreferenceManager
-                        .getDefaultSharedPreferences(mPhone.getContext());
-                if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
-                    sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
-                }
-                break;
-
-            case DctConstants.EVENT_NETWORK_STATUS_CHANGED:
-                int status = msg.arg1;
-                int cid = msg.arg2;
-                String url = (String) msg.obj;
-                onNetworkStatusChanged(status, cid, url);
-                break;
-
-            case DctConstants.EVENT_RADIO_AVAILABLE:
-                onRadioAvailable();
-                break;
-
-            case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
-                onRadioOffOrNotAvailable();
-                break;
-
-            case DctConstants.EVENT_DATA_SETUP_COMPLETE:
-                ar = (AsyncResult) msg.obj;
-                pair = (Pair<ApnContext, Integer>) ar.userObj;
-                apnContext = pair.first;
-                generation = pair.second;
-                requestType = msg.arg1;
-                handoverFailureMode = msg.arg2;
-                if (apnContext.getConnectionGeneration() == generation) {
-                    boolean success = true;
-                    int cause = DataFailCause.UNKNOWN;
-                    if (ar.exception != null) {
-                        success = false;
-                        cause = (int) ar.result;
-                    }
-                    onDataSetupComplete(apnContext, success, cause, requestType,
-                            handoverFailureMode);
-                } else {
-                    loge("EVENT_DATA_SETUP_COMPLETE: Dropped the event because generation "
-                            + "did not match.");
-                }
-                break;
-
-            case DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR:
-                ar = (AsyncResult) msg.obj;
-                pair = (Pair<ApnContext, Integer>) ar.userObj;
-                apnContext = pair.first;
-                generation = pair.second;
-                handoverFailureMode = msg.arg2;
-                if (apnContext.getConnectionGeneration() == generation) {
-                    onDataSetupCompleteError(apnContext, handoverFailureMode, false);
-                } else {
-                    loge("EVENT_DATA_SETUP_COMPLETE_ERROR: Dropped the event because generation "
-                            + "did not match.");
-                }
-                break;
-
-            case DctConstants.EVENT_DISCONNECT_DONE:
-                log("EVENT_DISCONNECT_DONE msg=" + msg);
-                ar = (AsyncResult) msg.obj;
-                pair = (Pair<ApnContext, Integer>) ar.userObj;
-                apnContext = pair.first;
-                generation = pair.second;
-                if (apnContext.getConnectionGeneration() == generation) {
-                    onDisconnectDone(apnContext);
-                } else {
-                    loge("EVENT_DISCONNECT_DONE: Dropped the event because generation "
-                            + "did not match.");
-                }
-                break;
-
-            case DctConstants.EVENT_VOICE_CALL_STARTED:
-                onVoiceCallStarted();
-                break;
-
-            case DctConstants.EVENT_VOICE_CALL_ENDED:
-                onVoiceCallEnded();
-                break;
-            case DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: {
-                sEnableFailFastRefCounter += (msg.arg1 == DctConstants.ENABLED) ? 1 : -1;
-                if (DBG) {
-                    log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
-                            + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
-                }
-                if (sEnableFailFastRefCounter < 0) {
-                    final String s = "CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: "
-                            + "sEnableFailFastRefCounter:" + sEnableFailFastRefCounter + " < 0";
-                    loge(s);
-                    sEnableFailFastRefCounter = 0;
-                }
-                final boolean enabled = sEnableFailFastRefCounter > 0;
-                if (DBG) {
-                    log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: enabled=" + enabled
-                            + " sEnableFailFastRefCounter=" + sEnableFailFastRefCounter);
-                }
-                if (mFailFast != enabled) {
-                    mFailFast = enabled;
-
-                    mDataStallNoRxEnabled = !enabled;
-                    if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
-                            && isAnyDataConnected()
-                            && (!mInVoiceCall ||
-                                    mPhone.getServiceStateTracker()
-                                        .isConcurrentVoiceAndDataAllowed())) {
-                        if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: start data stall");
-                        stopDataStallAlarm();
-                        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-                    } else {
-                        if (DBG) log("CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA: stop data stall");
-                        stopDataStallAlarm();
-                    }
-                }
-
-                break;
-            }
-            case DctConstants.CMD_ENABLE_MOBILE_PROVISIONING: {
-                Bundle bundle = msg.getData();
-                if (bundle != null) {
-                    try {
-                        mProvisioningUrl = (String)bundle.get(DctConstants.PROVISIONING_URL_KEY);
-                    } catch(ClassCastException e) {
-                        loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url not a string" + e);
-                        mProvisioningUrl = null;
-                    }
-                }
-                if (TextUtils.isEmpty(mProvisioningUrl)) {
-                    loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioning url is empty, ignoring");
-                    mIsProvisioning = false;
-                    mProvisioningUrl = null;
-                } else {
-                    loge("CMD_ENABLE_MOBILE_PROVISIONING: provisioningUrl=" + mProvisioningUrl);
-                    mIsProvisioning = true;
-                    startProvisioningApnAlarm();
-                }
-                break;
-            }
-            case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
-                if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
-                ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
-                if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
-                    if (mProvisioningApnAlarmTag == msg.arg1) {
-                        if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
-                        mIsProvisioning = false;
-                        mProvisioningUrl = null;
-                        stopProvisioningApnAlarm();
-                        cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnCtx);
-                    } else {
-                        if (DBG) {
-                            log("EVENT_PROVISIONING_APN_ALARM: ignore stale tag,"
-                                    + " mProvisioningApnAlarmTag:" + mProvisioningApnAlarmTag
-                                    + " != arg1:" + msg.arg1);
-                        }
-                    }
-                } else {
-                    if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Not connected ignore");
-                }
-                break;
-            }
-            case DctConstants.CMD_IS_PROVISIONING_APN: {
-                if (DBG) log("CMD_IS_PROVISIONING_APN");
-                boolean isProvApn;
-                try {
-                    String apnType = null;
-                    Bundle bundle = msg.getData();
-                    if (bundle != null) {
-                        apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
-                    }
-                    if (TextUtils.isEmpty(apnType)) {
-                        loge("CMD_IS_PROVISIONING_APN: apnType is empty");
-                        isProvApn = false;
-                    } else {
-                        isProvApn = isProvisioningApn(apnType);
-                    }
-                } catch (ClassCastException e) {
-                    loge("CMD_IS_PROVISIONING_APN: NO provisioning url ignoring");
-                    isProvApn = false;
-                }
-                if (DBG) log("CMD_IS_PROVISIONING_APN: ret=" + isProvApn);
-                mReplyAc.replyToMessage(msg, DctConstants.CMD_IS_PROVISIONING_APN,
-                        isProvApn ? DctConstants.ENABLED : DctConstants.DISABLED);
-                break;
-            }
-            case DctConstants.EVENT_RESTART_RADIO: {
-                restartRadio();
-                break;
-            }
-            case DctConstants.CMD_NET_STAT_POLL: {
-                if (msg.arg1 == DctConstants.ENABLED) {
-                    handleStartNetStatPoll((DctConstants.Activity)msg.obj);
-                } else if (msg.arg1 == DctConstants.DISABLED) {
-                    handleStopNetStatPoll((DctConstants.Activity)msg.obj);
-                }
-                break;
-            }
-            case DctConstants.EVENT_PCO_DATA_RECEIVED: {
-                handlePcoData((AsyncResult)msg.obj);
-                break;
-            }
-            case DctConstants.EVENT_DATA_RECONNECT:
-                if (DBG) {
-                    log("EVENT_DATA_RECONNECT: subId=" + msg.arg1 + ", type="
-                            + requestTypeToString(msg.arg2));
-                }
-                onDataReconnect((ApnContext) msg.obj, msg.arg1, msg.arg2);
-                break;
-            case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
-                onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
-                break;
-            case DctConstants.EVENT_DATA_ENABLED_CHANGED:
-                ar = (AsyncResult) msg.obj;
-                if (ar.result instanceof Pair) {
-                    Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
-                    boolean enabled = p.first;
-                    int reason = p.second;
-                    onDataEnabledChanged(enabled, reason);
-                }
-                break;
-            case DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED:
-                onDataEnabledOverrideRulesChanged();
-                break;
-            case DctConstants.EVENT_NR_TIMER_WATCHDOG:
-                mWatchdog = false;
-                reevaluateUnmeteredConnections();
-                break;
-            case DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED:
-                reevaluateCongestedConnections();
-                reevaluateUnmeteredConnections();
-                break;
-            case DctConstants.EVENT_CARRIER_CONFIG_CHANGED:
-                onCarrierConfigChanged();
-                break;
-            case DctConstants.EVENT_SIM_STATE_UPDATED:
-                int simState = msg.arg1;
-                onSimStateUpdated(simState);
-                break;
-            case DctConstants.EVENT_APN_UNTHROTTLED:
-                ar = (AsyncResult) msg.obj;
-                String apn = (String) ar.result;
-                onApnUnthrottled(apn);
-                break;
-            case DctConstants.EVENT_TRAFFIC_DESCRIPTORS_UPDATED:
-                onTrafficDescriptorsUpdated();
-                break;
-            default:
-                Rlog.e("DcTracker", "Unhandled event=" + msg);
-                break;
-
-        }
-    }
-
-    private int getApnProfileID(String apnType) {
-        if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
-            return RILConstants.DATA_PROFILE_IMS;
-        } else if (TextUtils.equals(apnType, ApnSetting.TYPE_FOTA_STRING)) {
-            return RILConstants.DATA_PROFILE_FOTA;
-        } else if (TextUtils.equals(apnType, ApnSetting.TYPE_CBS_STRING)) {
-            return RILConstants.DATA_PROFILE_CBS;
-        } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IA_STRING)) {
-            return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
-        } else if (TextUtils.equals(apnType, ApnSetting.TYPE_DUN_STRING)) {
-            return RILConstants.DATA_PROFILE_TETHERED;
-        } else {
-            return RILConstants.DATA_PROFILE_DEFAULT;
-        }
-    }
-
-    private int getCellLocationId() {
-        int cid = -1;
-        CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation();
-
-        if (loc != null) {
-            if (loc instanceof GsmCellLocation) {
-                cid = ((GsmCellLocation)loc).getCid();
-            } else if (loc instanceof CdmaCellLocation) {
-                cid = ((CdmaCellLocation)loc).getBaseStationId();
-            }
-        }
-        return cid;
-    }
-
-    /**
-     * Update link bandwidth estimate default values from carrier config.
-     * @param bandwidths String array of "RAT:upstream,downstream" for each RAT
-     * @param useLte For NR NSA, whether to use LTE value for upstream or not
-     */
-    private void updateLinkBandwidths(String[] bandwidths, boolean useLte) {
-        ConcurrentHashMap<String, Pair<Integer, Integer>> temp = new ConcurrentHashMap<>();
-        for (String config : bandwidths) {
-            int downstream = 14;
-            int upstream = 14;
-            String[] kv = config.split(":");
-            if (kv.length == 2) {
-                String[] split = kv[1].split(",");
-                if (split.length == 2) {
-                    try {
-                        downstream = Integer.parseInt(split[0]);
-                        upstream = Integer.parseInt(split[1]);
-                    } catch (NumberFormatException ignored) {
-                    }
-                }
-                temp.put(kv[0], new Pair<>(downstream, upstream));
-            }
-        }
-        if (useLte) {
-            Pair<Integer, Integer> ltePair =
-                    temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_LTE);
-            if (ltePair != null) {
-                if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA)) {
-                    temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA, new Pair<>(
-                            temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA).first,
-                            ltePair.second));
-                }
-                if (temp.containsKey(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE)) {
-                    temp.put(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE, new Pair<>(
-                            temp.get(DataConfigManager.DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE)
-                                    .first, ltePair.second));
-                }
-            }
-        }
-        mBandwidths = temp;
-        for (DataConnection dc : mDataConnections.values()) {
-            dc.sendMessage(DataConnection.EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED);
-        }
-    }
-
-    /**
-     * Return the link upstream/downstream values from CarrierConfig for the given RAT name.
-     * @param ratName RAT name from ServiceState#rilRadioTechnologyToString.
-     * @return pair of downstream/upstream values (kbps), or null if the config is not defined.
-     */
-    public Pair<Integer, Integer> getLinkBandwidthsFromCarrierConfig(String ratName) {
-        return mBandwidths.get(ratName);
-    }
-
-    @VisibleForTesting
-    public boolean shouldAutoAttach() {
-        if (mAutoAttachEnabled.get()) return true;
-
-        PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
-        ServiceState serviceState = mPhone.getServiceState();
-
-        if (phoneSwitcher == null || serviceState == null) return false;
-
-        // If voice is also not in service, don't auto attach.
-        if (serviceState.getState() != ServiceState.STATE_IN_SERVICE) return false;
-
-        // If voice is on LTE or NR, don't auto attach as for LTE / NR data would be attached.
-        if (serviceState.getVoiceNetworkType() == NETWORK_TYPE_LTE
-                || serviceState.getVoiceNetworkType() == NETWORK_TYPE_NR) return false;
-
-        // If phone is non default phone, modem may have detached from data for optimization.
-        // If phone is in voice call, for DSDS case DDS switch may be limited so we do try our
-        // best to setup data connection and allow auto-attach.
-        return (mPhone.getPhoneId() != phoneSwitcher.getPreferredDataPhoneId()
-                || mPhone.getState() != PhoneConstants.State.IDLE);
-    }
-
-    private void notifyAllDataDisconnected() {
-        sEnableFailFastRefCounter = 0;
-        mFailFast = false;
-        log("notify all data disconnected");
-        mAllDataDisconnectedRegistrants.notifyRegistrants();
-    }
-
-    public void registerForAllDataDisconnected(Handler h, int what) {
-        mAllDataDisconnectedRegistrants.addUnique(h, what, null);
-
-        if (areAllDataDisconnected()) {
-            notifyAllDataDisconnected();
-        }
-    }
-
-    public void unregisterForAllDataDisconnected(Handler h) {
-        mAllDataDisconnectedRegistrants.remove(h);
-    }
-
-    private void onDataEnabledChanged(boolean enable,
-                                      @DataEnabledChangedReason int enabledChangedReason) {
-        if (DBG) {
-            log("onDataEnabledChanged: enable=" + enable + ", enabledChangedReason="
-                    + enabledChangedReason);
-        }
-
-        if (enable) {
-            reevaluateDataConnections();
-            setupDataOnAllConnectableApns(Phone.REASON_DATA_ENABLED, RetryFailures.ALWAYS);
-        } else {
-            String cleanupReason;
-            switch (enabledChangedReason) {
-                case DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED:
-                    cleanupReason = Phone.REASON_DATA_DISABLED_INTERNAL;
-                    break;
-                case DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER:
-                    cleanupReason = Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN;
-                    break;
-                case DataEnabledSettings.REASON_USER_DATA_ENABLED:
-                case DataEnabledSettings.REASON_POLICY_DATA_ENABLED:
-                case DataEnabledSettings.REASON_PROVISIONED_CHANGED:
-                case DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED:
-                default:
-                    cleanupReason = Phone.REASON_DATA_SPECIFIC_DISABLED;
-                    break;
-
-            }
-            cleanUpAllConnectionsInternal(true, cleanupReason);
-        }
-    }
-
-    private void reevaluateCongestedConnections() {
-        log("reevaluateCongestedConnections");
-        int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
-        // congested override and either network is specified or unknown and all networks specified
-        boolean isCongested = mCongestedOverride && (mCongestedNetworkTypes.contains(rat)
-                || mCongestedNetworkTypes.containsAll(Arrays.stream(
-                TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet())));
-        for (DataConnection dataConnection : mDataConnections.values()) {
-            dataConnection.onCongestednessChanged(isCongested);
-        }
-    }
-
-    private void reevaluateUnmeteredConnections() {
-        log("reevaluateUnmeteredConnections");
-        int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
-        if (isNrUnmetered() && (!mPhone.getServiceState().getRoaming() || mNrNsaRoamingUnmetered)) {
-            setDataConnectionUnmetered(true);
-            if (!mWatchdog) {
-                startWatchdogAlarm();
-            }
-        } else {
-            stopWatchdogAlarm();
-            setDataConnectionUnmetered(isNetworkTypeUnmetered(rat));
-        }
-    }
-
-    private void setDataConnectionUnmetered(boolean isUnmetered) {
-        if (!isUnmetered || isTempNotMeteredSupportedByCarrier()) {
-            for (DataConnection dataConnection : mDataConnections.values()) {
-                dataConnection.onMeterednessChanged(isUnmetered);
-            }
-        }
-    }
-
-    private boolean isNetworkTypeUnmetered(@NetworkType int networkType) {
-        boolean isUnmetered;
-        if (mUnmeteredNetworkTypes == null || !mUnmeteredOverride) {
-            // check SubscriptionPlans if override is not defined
-            isUnmetered = isNetworkTypeUnmeteredViaSubscriptionPlan(networkType);
-            log("isNetworkTypeUnmeteredViaSubscriptionPlan: networkType=" + networkType
-                    + ", isUnmetered=" + isUnmetered);
-            return isUnmetered;
-        }
-        // unmetered override and either network is specified or unknown and all networks specified
-        isUnmetered = mUnmeteredNetworkTypes.contains(networkType)
-                || mUnmeteredNetworkTypes.containsAll(Arrays.stream(
-                TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet()));
-        if (DBG) {
-            log("isNetworkTypeUnmetered: networkType=" + networkType
-                    + ", isUnmetered=" + isUnmetered);
-        }
-        return isUnmetered;
-    }
-
-    private boolean isNetworkTypeUnmeteredViaSubscriptionPlan(@NetworkType int networkType) {
-        if (mSubscriptionPlans.isEmpty()) {
-            // safe return false if unable to get subscription plans or plans don't exist
-            return false;
-        }
-
-        boolean isGeneralUnmetered = true;
-        Set<Integer> allNetworkTypes = Arrays.stream(TelephonyManager.getAllNetworkTypes())
-                .boxed().collect(Collectors.toSet());
-        for (SubscriptionPlan plan : mSubscriptionPlans) {
-            // check plan is general (applies to all network types) or specific
-            if (Arrays.stream(plan.getNetworkTypes()).boxed().collect(Collectors.toSet())
-                    .containsAll(allNetworkTypes)) {
-                if (!isPlanUnmetered(plan)) {
-                    // metered takes precedence over unmetered for safety
-                    isGeneralUnmetered = false;
-                }
-            } else {
-                // check plan applies to given network type
-                if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
-                    for (int planNetworkType : plan.getNetworkTypes()) {
-                        if (planNetworkType == networkType) {
-                            return isPlanUnmetered(plan);
-                        }
-                    }
-                }
-            }
-        }
-        return isGeneralUnmetered;
-    }
-
-    private boolean isPlanUnmetered(SubscriptionPlan plan) {
-        return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED;
-    }
-
-    private boolean isNrUnmetered() {
-        int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
-        int override = mPhone.getDisplayInfoController().getTelephonyDisplayInfo()
-                .getOverrideNetworkType();
-
-        if (isNetworkTypeUnmetered(NETWORK_TYPE_NR)) {
-            if (mNrNsaMmwaveUnmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
-                    if (DBG) log("NR unmetered for mmwave only");
-                    return true;
-                }
-                return false;
-            } else if (mNrNsaSub6Unmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
-                    if (DBG) log("NR unmetered for sub6 only");
-                    return true;
-                }
-                return false;
-            }
-            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
-                    || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
-                    || rat == NETWORK_TYPE_NR) {
-                if (DBG) log("NR unmetered for all frequencies");
-                return true;
-            }
-            return false;
-        }
-
-        if (mNrNsaAllUnmetered) {
-            if (mNrNsaMmwaveUnmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
-                    if (DBG) log("NR NSA unmetered for mmwave only via carrier configs");
-                    return true;
-                }
-                return false;
-            } else if (mNrNsaSub6Unmetered) {
-                if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
-                    if (DBG) log("NR NSA unmetered for sub6 only via carrier configs");
-                    return true;
-                }
-                return false;
-            }
-            if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
-                    || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
-                if (DBG) log("NR NSA unmetered for all frequencies via carrier configs");
-                return true;
-            }
-            return false;
-        }
-
-        if (mNrSaAllUnmetered) {
-            // TODO: add logic for mNrSaMmwaveUnmetered and mNrSaSub6Unmetered once it's defined
-            // in TelephonyDisplayInfo
-            if (rat == NETWORK_TYPE_NR) {
-                if (DBG) log("NR SA unmetered for all frequencies via carrier configs");
-                return true;
-            }
-            return false;
-        }
-
-        return false;
-    }
-
-    private boolean isTempNotMeteredSupportedByCarrier() {
-        CarrierConfigManager configManager =
-                mPhone.getContext().getSystemService(CarrierConfigManager.class);
-        if (configManager != null) {
-            PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
-            if (bundle != null) {
-                return bundle.getBoolean(
-                        CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL);
-            }
-        }
-
-        return false;
-    }
-
-    protected void log(String s) {
-        Rlog.d(mLogTag, s);
-    }
-
-    private void loge(String s) {
-        Rlog.e(mLogTag, s);
-    }
-
-    private void logSortedApnContexts() {
-        if (VDBG) {
-            log("initApnContexts: X mApnContexts=" + mApnContexts);
-
-            StringBuilder sb = new StringBuilder();
-            sb.append("sorted apncontexts -> [");
-            for (ApnContext apnContext : mPrioritySortedApnContexts) {
-                sb.append(apnContext);
-                sb.append(", ");
-
-                log("sorted list");
-            }
-            sb.append("]");
-            log(sb.toString());
-        }
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("DcTracker:");
-        pw.println(" RADIO_TESTS=" + RADIO_TESTS);
-        pw.println(" mDataEnabledSettings=" + mDataEnabledSettings);
-        pw.println(" isDataAllowed=" + isDataAllowed(null));
-        pw.flush();
-        pw.println(" mRequestedApnType=" + mRequestedApnType);
-        pw.println(" mPhone=" + mPhone.getPhoneName());
-        pw.println(" mConfigReady=" + mConfigReady);
-        pw.println(" mSimState=" + SubscriptionInfoUpdater.simStateString(mSimState));
-        pw.println(" mActivity=" + mActivity);
-        pw.println(" mState=" + mState);
-        pw.println(" mTxPkts=" + mTxPkts);
-        pw.println(" mRxPkts=" + mRxPkts);
-        pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
-        pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
-        pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
-        pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
-        pw.println(" mDataStallNoRxEnabled=" + mDataStallNoRxEnabled);
-        pw.println(" mEmergencyApn=" + mEmergencyApn);
-        pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
-        pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
-        pw.println(" mResolver=" + mResolver);
-        pw.println(" mReconnectIntent=" + mReconnectIntent);
-        pw.println(" mAutoAttachEnabled=" + mAutoAttachEnabled.get());
-        pw.println(" mIsScreenOn=" + mIsScreenOn);
-        pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
-        pw.println(" mDataServiceBound=" + mDataServiceBound);
-        pw.println(" mDataRoamingLeakageLog= ");
-        mDataRoamingLeakageLog.dump(fd, pw, args);
-        pw.println(" mApnSettingsInitializationLog= ");
-        mApnSettingsInitializationLog.dump(fd, pw, args);
-        pw.flush();
-        pw.println(" ***************************************");
-        DcController dcc = mDcc;
-        if (dcc != null) {
-            if (mDataServiceBound) {
-                dcc.dump(fd, pw, args);
-            } else {
-                pw.println(" Can't dump mDcc because data service is not bound.");
-            }
-        } else {
-            pw.println(" mDcc=null");
-        }
-        pw.println(" ***************************************");
-        HashMap<Integer, DataConnection> dcs = mDataConnections;
-        if (dcs != null) {
-            Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet();
-            pw.println(" mDataConnections: count=" + mDcSet.size());
-            for (Entry<Integer, DataConnection> entry : mDcSet) {
-                pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
-                entry.getValue().dump(fd, pw, args);
-            }
-        } else {
-            pw.println("mDataConnections=null");
-        }
-        pw.println(" ***************************************");
-        pw.flush();
-        HashMap<String, Integer> apnToDcId = mApnToDataConnectionId;
-        if (apnToDcId != null) {
-            Set<Entry<String, Integer>> apnToDcIdSet = apnToDcId.entrySet();
-            pw.println(" mApnToDataConnectonId size=" + apnToDcIdSet.size());
-            for (Entry<String, Integer> entry : apnToDcIdSet) {
-                pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
-            }
-        } else {
-            pw.println("mApnToDataConnectionId=null");
-        }
-        pw.println(" ***************************************");
-        pw.flush();
-        ConcurrentHashMap<String, ApnContext> apnCtxs = mApnContexts;
-        if (apnCtxs != null) {
-            Set<Entry<String, ApnContext>> apnCtxsSet = apnCtxs.entrySet();
-            pw.println(" mApnContexts size=" + apnCtxsSet.size());
-            for (Entry<String, ApnContext> entry : apnCtxsSet) {
-                entry.getValue().dump(fd, pw, args);
-            }
-            ApnContext.dumpLocalLog(fd, pw, args);
-            pw.println(" ***************************************");
-        } else {
-            pw.println(" mApnContexts=null");
-        }
-        pw.flush();
-
-        pw.println(" mAllApnSettings size=" + mAllApnSettings.size());
-        for (int i = 0; i < mAllApnSettings.size(); i++) {
-            pw.printf(" mAllApnSettings[%d]: %s\n", i, mAllApnSettings.get(i));
-        }
-        pw.flush();
-
-        pw.println(" mPreferredApn=" + mPreferredApn);
-        pw.println(" mIsPsRestricted=" + mIsPsRestricted);
-        pw.println(" mIsDisposed=" + mIsDisposed);
-        pw.println(" mIntentReceiver=" + mIntentReceiver);
-        pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
-        pw.println(" canSetPreferApn=" + mCanSetPreferApn);
-        pw.println(" mApnObserver=" + mApnObserver);
-        pw.println(" isAnyDataConnected=" + isAnyDataConnected());
-        pw.println(" mAttached=" + mAttached.get());
-        mDataEnabledSettings.dump(fd, pw, args);
-        pw.flush();
-    }
-
-    public String[] getPcscfAddress(String apnType) {
-        log("getPcscfAddress()");
-        ApnContext apnContext = null;
-
-        if(apnType == null){
-            log("apnType is null, return null");
-            return null;
-        }
-
-        if (TextUtils.equals(apnType, ApnSetting.TYPE_EMERGENCY_STRING)) {
-            apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
-        } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
-            apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
-        } else {
-            log("apnType is invalid, return null");
-            return null;
-        }
-
-        if (apnContext == null) {
-            log("apnContext is null, return null");
-            return null;
-        }
-
-        DataConnection dataConnection = apnContext.getDataConnection();
-        String[] result = null;
-
-        if (dataConnection != null) {
-            result = dataConnection.getPcscfAddresses();
-
-            if (result != null) {
-                for (int i = 0; i < result.length; i++) {
-                    log("Pcscf[" + i + "]: " + result[i]);
-                }
-            }
-            return result;
-        }
-        return null;
-    }
-
-    /**
-     * Create default apn settings for the apn type like emergency, and ims
-     */
-    private ApnSetting buildDefaultApnSetting(@NonNull String entry,
-            @NonNull String apn, @ApnType int apnTypeBitmask) {
-        return new ApnSetting.Builder()
-                .setEntryName(entry)
-                .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
-                .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
-                .setApnName(apn)
-                .setApnTypeBitmask(apnTypeBitmask)
-                .setCarrierEnabled(true)
-                .setApnSetId(Telephony.Carriers.MATCH_ALL_APN_SET_ID)
-                .build();
-    }
-
-    /**
-     * Add default APN settings to APN settings list as needed
-     */
-    private void addDefaultApnSettingsAsNeeded() {
-        boolean isEmergencyApnConfigured = false;
-        boolean isImsApnConfigured = false;
-
-        for (ApnSetting apn : mAllApnSettings) {
-            if (apn.canHandleType(ApnSetting.TYPE_EMERGENCY)) {
-                isEmergencyApnConfigured = true;
-            }
-            if (apn.canHandleType(ApnSetting.TYPE_IMS)) {
-                isImsApnConfigured = true;
-            }
-            if (isEmergencyApnConfigured && isImsApnConfigured) {
-                log("Both emergency and ims apn setting are already present");
-                return;
-            }
-        }
-
-        // Add default apn setting for emergency service if it is not present
-        if (!isEmergencyApnConfigured) {
-            mAllApnSettings.add(buildDefaultApnSetting(
-                    "DEFAULT EIMS", "sos", ApnSetting.TYPE_EMERGENCY));
-            log("default emergency apn is created");
-        }
-
-        // Only add default apn setting for ims when it is not present and sim is loaded
-        if (!isImsApnConfigured && mSimState == TelephonyManager.SIM_STATE_LOADED) {
-            mAllApnSettings.add(buildDefaultApnSetting(
-                    "DEFAULT IMS", "ims", ApnSetting.TYPE_IMS));
-            log("default ims apn is created");
-        }
-    }
-
-    private void cleanUpConnectionsOnUpdatedApns(boolean detach, String reason) {
-        if (DBG) log("cleanUpConnectionsOnUpdatedApns: detach=" + detach);
-        if (mAllApnSettings.isEmpty()) {
-            cleanUpAllConnectionsInternal(detach, Phone.REASON_APN_CHANGED);
-        } else {
-            if (getDataRat() == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
-                // unknown rat is an exception for data rat change. Its only received when out of
-                // service and is not applicable for apn bearer bitmask. We should bypass the check
-                // of waiting apn list and keep the data connection on.
-                return;
-            }
-            for (ApnContext apnContext : mApnContexts.values()) {
-                boolean cleanupRequired = true;
-                if (!apnContext.isDisconnected()) {
-                    ArrayList<ApnSetting> waitingApns = buildWaitingApns(
-                            apnContext.getApnType(), getDataRat());
-                    if (apnContext.getWaitingApns().size() != waitingApns.size()
-                            || !apnContext.getWaitingApns().containsAll(waitingApns)) {
-                        apnContext.setWaitingApns(waitingApns);
-                    }
-                    for (ApnSetting apnSetting : waitingApns) {
-                        if (areCompatible(apnSetting, apnContext.getApnSetting())) {
-                            cleanupRequired = false;
-                            break;
-                        }
-                    }
-
-                    if (cleanupRequired) {
-                        if (DBG) {
-                            log("cleanUpConnectionsOnUpdatedApns: APN type "
-                                    + apnContext.getApnType() + " clean up is required. The new "
-                                    + "waiting APN list " + waitingApns + " does not cover "
-                                    + apnContext.getApnSetting());
-                        }
-                        apnContext.setReason(reason);
-                        cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
-                    }
-                }
-            }
-        }
-
-        if (!isAnyDataConnected()) {
-            stopNetStatPoll();
-            stopDataStallAlarm();
-        }
-
-        mRequestedApnType = ApnSetting.TYPE_DEFAULT;
-
-        if (areAllDataDisconnected()) {
-            notifyAllDataDisconnected();
-        }
-    }
-
-    /**
-     * Polling stuff
-     */
-    protected void resetPollStats() {
-        mTxPkts = -1;
-        mRxPkts = -1;
-        mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
-    }
-
-    protected void startNetStatPoll() {
-        if (isAnyDataConnected() && !mNetStatPollEnabled) {
-            if (DBG) {
-                log("startNetStatPoll");
-            }
-            resetPollStats();
-            mNetStatPollEnabled = true;
-            mPollNetStat.run();
-        }
-        if (mPhone != null) {
-            mPhone.notifyDataActivity();
-        }
-    }
-
-    protected void stopNetStatPoll() {
-        mNetStatPollEnabled = false;
-        removeCallbacks(mPollNetStat);
-        if (DBG) {
-            log("stopNetStatPoll");
-        }
-
-        // To sync data activity icon in the case of switching data connection to send MMS.
-        if (mPhone != null) {
-            mPhone.notifyDataActivity();
-        }
-    }
-
-    public void sendStartNetStatPoll(DctConstants.Activity activity) {
-        Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
-        msg.arg1 = DctConstants.ENABLED;
-        msg.obj = activity;
-        sendMessage(msg);
-    }
-
-    private void handleStartNetStatPoll(DctConstants.Activity activity) {
-        startNetStatPoll();
-        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-        setActivity(activity);
-    }
-
-    public void sendStopNetStatPoll(DctConstants.Activity activity) {
-        Message msg = obtainMessage(DctConstants.CMD_NET_STAT_POLL);
-        msg.arg1 = DctConstants.DISABLED;
-        msg.obj = activity;
-        sendMessage(msg);
-    }
-
-    private void handleStopNetStatPoll(DctConstants.Activity activity) {
-        stopNetStatPoll();
-        stopDataStallAlarm();
-        setActivity(activity);
-    }
-
-    private void onDataEnabledOverrideRulesChanged() {
-        if (DBG) {
-            log("onDataEnabledOverrideRulesChanged");
-        }
-
-        for (ApnContext apnContext : mPrioritySortedApnContexts) {
-            if (isDataAllowed(apnContext, REQUEST_TYPE_NORMAL, null)) {
-                if (apnContext.getDataConnection() != null) {
-                    apnContext.getDataConnection().reevaluateRestrictedState();
-                }
-                setupDataOnConnectableApn(apnContext, Phone.REASON_DATA_ENABLED_OVERRIDE,
-                        RetryFailures.ALWAYS);
-            } else if (shouldCleanUpConnection(apnContext, true, false)) {
-                apnContext.setReason(Phone.REASON_DATA_ENABLED_OVERRIDE);
-                cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, apnContext);
-            }
-        }
-    }
-
-    private void updateDataActivity() {
-        long sent, received;
-
-        DctConstants.Activity newActivity;
-
-        TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
-        TxRxSum curTxRxSum = new TxRxSum();
-        curTxRxSum.updateTotalTxRxSum();
-        mTxPkts = curTxRxSum.txPkts;
-        mRxPkts = curTxRxSum.rxPkts;
-
-        if (VDBG) {
-            log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
-        }
-
-        if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
-            sent = mTxPkts - preTxRxSum.txPkts;
-            received = mRxPkts - preTxRxSum.rxPkts;
-
-            if (VDBG)
-                log("updateDataActivity: sent=" + sent + " received=" + received);
-            if (sent > 0 && received > 0) {
-                newActivity = DctConstants.Activity.DATAINANDOUT;
-            } else if (sent > 0 && received == 0) {
-                newActivity = DctConstants.Activity.DATAOUT;
-            } else if (sent == 0 && received > 0) {
-                newActivity = DctConstants.Activity.DATAIN;
-            } else {
-                newActivity = (mActivity == DctConstants.Activity.DORMANT) ?
-                        mActivity : DctConstants.Activity.NONE;
-            }
-
-            if (mActivity != newActivity && mIsScreenOn) {
-                if (VDBG)
-                    log("updateDataActivity: newActivity=" + newActivity);
-                mActivity = newActivity;
-                mPhone.notifyDataActivity();
-            }
-        }
-    }
-
-    private void handlePcoData(AsyncResult ar) {
-        if (ar.exception != null) {
-            loge("PCO_DATA exception: " + ar.exception);
-            return;
-        }
-        PcoData pcoData = (PcoData)(ar.result);
-        ArrayList<DataConnection> dcList = new ArrayList<>();
-        DataConnection temp = mDcc.getActiveDcByCid(pcoData.cid);
-        if (temp != null) {
-            dcList.add(temp);
-        }
-        if (dcList.size() == 0) {
-            loge("PCO_DATA for unknown cid: " + pcoData.cid + ", inferring");
-            for (DataConnection dc : mDataConnections.values()) {
-                final int cid = dc.getCid();
-                if (cid == pcoData.cid) {
-                    if (VDBG) log("  found " + dc);
-                    dcList.clear();
-                    dcList.add(dc);
-                    break;
-                }
-                // check if this dc is still connecting
-                if (cid == -1) {
-                    for (ApnContext apnContext : dc.getApnContexts()) {
-                        if (apnContext.getState() == DctConstants.State.CONNECTING) {
-                            if (VDBG) log("  found potential " + dc);
-                            dcList.add(dc);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        if (dcList.size() == 0) {
-            loge("PCO_DATA - couldn't infer cid");
-            return;
-        }
-        for (DataConnection dc : dcList) {
-            List<ApnContext> apnContextList = dc.getApnContexts();
-            if (apnContextList.size() == 0) {
-                break;
-            }
-            // send one out for each apn type in play
-            for (ApnContext apnContext : apnContextList) {
-                String apnType = apnContext.getApnType();
-
-                final Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
-                intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
-                        ApnSetting.getApnTypesBitmaskFromString(apnType));
-                intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
-                        ApnSetting.getProtocolIntFromString(pcoData.bearerProto));
-                intent.putExtra(TelephonyManager.EXTRA_PCO_ID, pcoData.pcoId);
-                intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, pcoData.contents);
-                mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
-            }
-        }
-    }
-
-    /**
-     * Data-Stall
-     */
-
-    // Recovery action taken in case of data stall
-    @IntDef(
-        value = {
-            RECOVERY_ACTION_GET_DATA_CALL_LIST,
-            RECOVERY_ACTION_CLEANUP,
-            RECOVERY_ACTION_REREGISTER,
-            RECOVERY_ACTION_RADIO_RESTART
-        })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RecoveryAction {};
-    private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST      = 0;
-    private static final int RECOVERY_ACTION_CLEANUP                 = 1;
-    private static final int RECOVERY_ACTION_REREGISTER              = 2;
-    private static final int RECOVERY_ACTION_RADIO_RESTART           = 3;
-
-    // Recovery handler class for cellular data stall
-    private class DataStallRecoveryHandler {
-        // Default minimum duration between each recovery steps
-        private static final int
-                DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS = (3 * 60 * 1000); // 3 mins
-
-        // The elapsed real time of last recovery attempted
-        private long mTimeLastRecoveryStartMs;
-        // Whether current network good or not
-        private boolean mIsValidNetwork;
-        // Whether data stall happened or not.
-        private boolean mWasDataStall;
-        // Whether the result of last action(RADIO_RESTART) reported.
-        private boolean mLastActionReported;
-        // The real time for data stall start.
-        private long mDataStallStartMs;
-        // Last data stall action.
-        private @RecoveryAction int mLastAction;
-
-        public DataStallRecoveryHandler() {
-            reset();
-        }
-
-        public void reset() {
-            mTimeLastRecoveryStartMs = 0;
-            putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
-        }
-
-        private void setNetworkValidationState(boolean isValid) {
-            // Validation status is true and was not data stall.
-            if (isValid && !mWasDataStall) {
-                return;
-            }
-
-            if (!mWasDataStall) {
-                mWasDataStall = true;
-                mDataStallStartMs = SystemClock.elapsedRealtime();
-                if (DBG) log("data stall: start time = " + mDataStallStartMs);
-                return;
-            }
-
-            if (!mLastActionReported) {
-                int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
-                if (DBG) {
-                    log("data stall: lastaction = " + mLastAction + ", isRecovered = "
-                            + isValid + ", mTimeDuration = " + timeDuration);
-                }
-                DataStallRecoveryStats.onDataStallEvent(mLastAction, mPhone, isValid,
-                                                        timeDuration);
-                mLastActionReported = true;
-            }
-
-            if (isValid) {
-                mLastActionReported = false;
-                mWasDataStall = false;
-            }
-        }
-
-        public boolean isAggressiveRecovery() {
-            @RecoveryAction int action = getRecoveryAction();
-
-            return ((action == RECOVERY_ACTION_CLEANUP)
-                    || (action == RECOVERY_ACTION_REREGISTER)
-                    || (action == RECOVERY_ACTION_RADIO_RESTART));
-        }
-
-        private long getMinDurationBetweenRecovery() {
-            return Settings.Global.getLong(mResolver,
-                Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
-                DEFAULT_MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS);
-        }
-
-        private long getElapsedTimeSinceRecoveryMs() {
-            return (SystemClock.elapsedRealtime() - mTimeLastRecoveryStartMs);
-        }
-
-        @RecoveryAction
-        private int getRecoveryAction() {
-            @RecoveryAction int action = Settings.System.getInt(mResolver,
-                    "radio.data.stall.recovery.action", RECOVERY_ACTION_GET_DATA_CALL_LIST);
-            if (VDBG_STALL) log("getRecoveryAction: " + action);
-            return action;
-        }
-
-        private void putRecoveryAction(@RecoveryAction int action) {
-            Settings.System.putInt(mResolver, "radio.data.stall.recovery.action", action);
-            if (VDBG_STALL) log("putRecoveryAction: " + action);
-        }
-
-        private void broadcastDataStallDetected(@RecoveryAction int recoveryAction) {
-            Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
-            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-            intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
-            mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
-        }
-
-        private boolean isRecoveryAlreadyStarted() {
-            return getRecoveryAction() != RECOVERY_ACTION_GET_DATA_CALL_LIST;
-        }
-
-        private boolean checkRecovery() {
-            // To avoid back to back recovery wait for a grace period
-            if (getElapsedTimeSinceRecoveryMs() < getMinDurationBetweenRecovery()) {
-                if (VDBG_STALL) log("skip back to back data stall recovery");
-                return false;
-            }
-
-            // Skip recovery if it can cause a call to drop
-            if (mPhone.getState() != PhoneConstants.State.IDLE
-                    && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) {
-                if (VDBG_STALL) log("skip data stall recovery as there is an active call");
-                return false;
-            }
-
-            // Allow recovery if data is expected to work
-            return mAttached.get() && isDataAllowed(null);
-        }
-
-        private void triggerRecovery() {
-            // Updating the recovery start time early to avoid race when
-            // the message is being processed in the Queue
-            mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
-            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
-        }
-
-        public void doRecovery() {
-            if (isAnyDataConnected()) {
-                // Go through a series of recovery steps, each action transitions to the next action
-                @RecoveryAction final int recoveryAction = getRecoveryAction();
-                final int signalStrength = mPhone.getSignalStrength().getLevel();
-                TelephonyMetrics.getInstance().writeSignalStrengthEvent(
-                        mPhone.getPhoneId(), signalStrength);
-                TelephonyMetrics.getInstance().writeDataStallEvent(
-                        mPhone.getPhoneId(), recoveryAction);
-                mLastAction = recoveryAction;
-                mLastActionReported = false;
-                broadcastDataStallDetected(recoveryAction);
-
-                switch (recoveryAction) {
-                    case RECOVERY_ACTION_GET_DATA_CALL_LIST:
-                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
-                            mSentSinceLastRecv);
-                        if (DBG) log("doRecovery() get data call list");
-                        mDataServiceManager.requestDataCallList(obtainMessage());
-                        putRecoveryAction(RECOVERY_ACTION_CLEANUP);
-                        break;
-                    case RECOVERY_ACTION_CLEANUP:
-                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP,
-                            mSentSinceLastRecv);
-                        if (DBG) log("doRecovery() cleanup all connections");
-                        cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
-                                ApnSetting.TYPE_DEFAULT)));
-                        cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
-                                ApnSetting.TYPE_ENTERPRISE)));
-                        putRecoveryAction(RECOVERY_ACTION_REREGISTER);
-                        break;
-                    case RECOVERY_ACTION_REREGISTER:
-                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
-                            mSentSinceLastRecv);
-                        if (DBG) log("doRecovery() re-register");
-                        mPhone.getServiceStateTracker().reRegisterNetwork(null);
-                        putRecoveryAction(RECOVERY_ACTION_RADIO_RESTART);
-                        break;
-                    case RECOVERY_ACTION_RADIO_RESTART:
-                        EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
-                            mSentSinceLastRecv);
-                        if (DBG) log("restarting radio");
-                        restartRadio();
-                        reset();
-                        break;
-                    default:
-                        throw new RuntimeException("doRecovery: Invalid recoveryAction="
-                            + recoveryAction);
-                }
-                mSentSinceLastRecv = 0;
-            }
-        }
-
-        public void processNetworkStatusChanged(boolean isValid) {
-            setNetworkValidationState(isValid);
-            if (isValid) {
-                mIsValidNetwork = true;
-                reset();
-            } else {
-                if (mIsValidNetwork || isRecoveryAlreadyStarted()) {
-                    mIsValidNetwork = false;
-                    // Check and trigger a recovery if network switched from good
-                    // to bad or recovery is already started before.
-                    if (checkRecovery()) {
-                        if (DBG) log("trigger data stall recovery");
-                        triggerRecovery();
-                    }
-                }
-            }
-        }
-
-        public boolean isRecoveryOnBadNetworkEnabled() {
-            return Settings.Global.getInt(mResolver,
-                    Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1) == 1;
-        }
-
-        public boolean isNoRxDataStallDetectionEnabled() {
-            return mDataStallNoRxEnabled && !isRecoveryOnBadNetworkEnabled();
-        }
-    }
-
-    private void updateDataStallInfo() {
-        long sent, received;
-
-        TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
-        mDataStallTxRxSum.updateTotalTxRxSum();
-
-        if (VDBG_STALL) {
-            log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
-                    " preTxRxSum=" + preTxRxSum);
-        }
-
-        sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
-        received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
-
-        if (RADIO_TESTS) {
-            if (SystemProperties.getBoolean("radio.test.data.stall", false)) {
-                log("updateDataStallInfo: radio.test.data.stall true received = 0;");
-                received = 0;
-            }
-        }
-        if ( sent > 0 && received > 0 ) {
-            if (VDBG_STALL) log("updateDataStallInfo: IN/OUT");
-            mSentSinceLastRecv = 0;
-            mDsRecoveryHandler.reset();
-        } else if (sent > 0 && received == 0) {
-            if (isPhoneStateIdle()) {
-                mSentSinceLastRecv += sent;
-            } else {
-                mSentSinceLastRecv = 0;
-            }
-            if (DBG) {
-                log("updateDataStallInfo: OUT sent=" + sent +
-                        " mSentSinceLastRecv=" + mSentSinceLastRecv);
-            }
-        } else if (sent == 0 && received > 0) {
-            if (VDBG_STALL) log("updateDataStallInfo: IN");
-            mSentSinceLastRecv = 0;
-            mDsRecoveryHandler.reset();
-        } else {
-            if (VDBG_STALL) log("updateDataStallInfo: NONE");
-        }
-    }
-
-    private boolean isPhoneStateIdle() {
-        for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
-            Phone phone = PhoneFactory.getPhone(i);
-            if (phone != null && phone.getState() != PhoneConstants.State.IDLE) {
-                log("isPhoneStateIdle false: Voice call active on phone " + i);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private void onDataStallAlarm(int tag) {
-        if (mDataStallAlarmTag != tag) {
-            if (DBG) {
-                log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
-            }
-            return;
-        }
-
-        if (DBG) log("Data stall alarm");
-        updateDataStallInfo();
-
-        int hangWatchdogTrigger = Settings.Global.getInt(mResolver,
-                Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
-                NUMBER_SENT_PACKETS_OF_HANG);
-
-        boolean suspectedStall = DATA_STALL_NOT_SUSPECTED;
-        if (mSentSinceLastRecv >= hangWatchdogTrigger) {
-            if (DBG) {
-                log("onDataStallAlarm: tag=" + tag + " do recovery action="
-                        + mDsRecoveryHandler.getRecoveryAction());
-            }
-            suspectedStall = DATA_STALL_SUSPECTED;
-            sendMessage(obtainMessage(DctConstants.EVENT_DO_RECOVERY));
-        } else {
-            if (VDBG_STALL) {
-                log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
-                    " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
-            }
-        }
-        startDataStallAlarm(suspectedStall);
-    }
-
-    protected void startDataStallAlarm(boolean suspectedStall) {
-        int delayInMs;
-
-        if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled() && isAnyDataConnected()) {
-            // If screen is on or data stall is currently suspected, set the alarm
-            // with an aggressive timeout.
-            if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) {
-                delayInMs = Settings.Global.getInt(mResolver,
-                        Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
-                        DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
-            } else {
-                delayInMs = Settings.Global.getInt(mResolver,
-                        Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
-                        DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT);
-            }
-
-            mDataStallAlarmTag += 1;
-            if (VDBG_STALL) {
-                log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
-                        " delay=" + (delayInMs / 1000) + "s");
-            }
-            Intent intent = new Intent(INTENT_DATA_STALL_ALARM);
-            intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TAG, mDataStallAlarmTag);
-            intent.putExtra(INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE, mTransportType);
-            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-            mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
-                    SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
-        } else {
-            if (VDBG_STALL) {
-                log("startDataStallAlarm: NOT started, no connection tag=" + mDataStallAlarmTag);
-            }
-        }
-    }
-
-    private void stopDataStallAlarm() {
-        if (VDBG_STALL) {
-            log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
-                    " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
-        }
-        mDataStallAlarmTag += 1;
-        if (mDataStallAlarmIntent != null) {
-            mAlarmManager.cancel(mDataStallAlarmIntent);
-            mDataStallAlarmIntent = null;
-        }
-    }
-
-    private void restartDataStallAlarm() {
-        if (!isAnyDataConnected()) return;
-        // To be called on screen status change.
-        // Do not cancel the alarm if it is set with aggressive timeout.
-        if (mDsRecoveryHandler.isAggressiveRecovery()) {
-            if (DBG) log("restartDataStallAlarm: action is pending. not resetting the alarm.");
-            return;
-        }
-        if (VDBG_STALL) log("restartDataStallAlarm: stop then start.");
-        stopDataStallAlarm();
-        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
-    }
-
-    /**
-     * Provisioning APN
-     */
-    private void onActionIntentProvisioningApnAlarm(Intent intent) {
-        if (DBG) log("onActionIntentProvisioningApnAlarm: action=" + intent.getAction());
-        Message msg = obtainMessage(DctConstants.EVENT_PROVISIONING_APN_ALARM,
-                intent.getAction());
-        msg.arg1 = intent.getIntExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, 0);
-        sendMessage(msg);
-    }
-
-    private void startProvisioningApnAlarm() {
-        int delayInMs = Settings.Global.getInt(mResolver,
-                                Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
-                                PROVISIONING_APN_ALARM_DELAY_IN_MS_DEFAULT);
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            // Allow debug code to use a system property to provide another value
-            String delayInMsStrg = Integer.toString(delayInMs);
-            delayInMsStrg = System.getProperty(DEBUG_PROV_APN_ALARM, delayInMsStrg);
-            try {
-                delayInMs = Integer.parseInt(delayInMsStrg);
-            } catch (NumberFormatException e) {
-                loge("startProvisioningApnAlarm: e=" + e);
-            }
-        }
-        mProvisioningApnAlarmTag += 1;
-        if (DBG) {
-            log("startProvisioningApnAlarm: tag=" + mProvisioningApnAlarmTag +
-                    " delay=" + (delayInMs / 1000) + "s");
-        }
-        Intent intent = new Intent(INTENT_PROVISIONING_APN_ALARM);
-        intent.putExtra(PROVISIONING_APN_ALARM_TAG_EXTRA, mProvisioningApnAlarmTag);
-        mProvisioningApnAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                SystemClock.elapsedRealtime() + delayInMs, mProvisioningApnAlarmIntent);
-    }
-
-    private void stopProvisioningApnAlarm() {
-        if (DBG) {
-            log("stopProvisioningApnAlarm: current tag=" + mProvisioningApnAlarmTag +
-                    " mProvsioningApnAlarmIntent=" + mProvisioningApnAlarmIntent);
-        }
-        mProvisioningApnAlarmTag += 1;
-        if (mProvisioningApnAlarmIntent != null) {
-            mAlarmManager.cancel(mProvisioningApnAlarmIntent);
-            mProvisioningApnAlarmIntent = null;
-        }
-    }
-
-    /**
-     * 5G connection reevaluation alarm
-     */
-    private void startWatchdogAlarm() {
-        sendMessageDelayed(obtainMessage(DctConstants.EVENT_NR_TIMER_WATCHDOG), mWatchdogTimeMs);
-        mWatchdog = true;
-    }
-
-    private void stopWatchdogAlarm() {
-        removeMessages(DctConstants.EVENT_NR_TIMER_WATCHDOG);
-        mWatchdog = false;
-    }
-
-    private void onDataServiceBindingChanged(boolean bound) {
-        if (!bound) {
-            if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                boolean connPersistenceOnRestart = mPhone.getContext().getResources()
-                   .getBoolean(com.android
-                       .internal.R.bool.config_wlan_data_service_conn_persistence_on_restart);
-                if (!connPersistenceOnRestart) {
-                    cleanUpAllConnectionsInternal(false, Phone.REASON_IWLAN_DATA_SERVICE_DIED);
-                }
-            }
-        } else {
-            //reset throttling after binding to data service
-            mDataThrottler.reset();
-        }
-        mDataServiceBound = bound;
-    }
-
-    public static String requestTypeToString(@RequestNetworkType int type) {
-        switch (type) {
-            case REQUEST_TYPE_NORMAL: return "NORMAL";
-            case REQUEST_TYPE_HANDOVER: return "HANDOVER";
-        }
-        return "UNKNOWN";
-    }
-
-    public static String releaseTypeToString(@ReleaseNetworkType int type) {
-        switch (type) {
-            case RELEASE_TYPE_NORMAL: return "NORMAL";
-            case RELEASE_TYPE_DETACH: return "DETACH";
-            case RELEASE_TYPE_HANDOVER: return "HANDOVER";
-        }
-        return "UNKNOWN";
-    }
-
-    @RilRadioTechnology
-    protected int getDataRat() {
-        ServiceState ss = mPhone.getServiceState();
-        NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
-        if (nrs != null) {
-            return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology());
-        }
-        return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-    }
-
-    @RilRadioTechnology
-    private int getVoiceRat() {
-        ServiceState ss = mPhone.getServiceState();
-        NetworkRegistrationInfo nrs = ss.getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_CS, mTransportType);
-        if (nrs != null) {
-            return ServiceState.networkTypeToRilRadioTechnology(nrs.getAccessNetworkTechnology());
-        }
-        return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-    }
-
-    private void read5GConfiguration() {
-        if (DBG) log("read5GConfiguration");
-        String[] bandwidths = CarrierConfigManager.getDefaultConfig().getStringArray(
-                CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY);
-        boolean useLte = false;
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
-            if (b != null) {
-                if (b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY) != null) {
-                    bandwidths = b.getStringArray(CarrierConfigManager.KEY_BANDWIDTH_STRING_ARRAY);
-                }
-                useLte = b.getBoolean(CarrierConfigManager
-                        .KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL);
-                mWatchdogTimeMs = b.getLong(CarrierConfigManager.KEY_5G_WATCHDOG_TIME_MS_LONG);
-                mNrNsaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL);
-                mNrNsaMmwaveUnmetered = b.getBoolean(
-                        CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL);
-                mNrNsaSub6Unmetered = b.getBoolean(
-                        CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL);
-                mNrSaAllUnmetered = b.getBoolean(CarrierConfigManager.KEY_UNMETERED_NR_SA_BOOL);
-                mNrSaMmwaveUnmetered = b.getBoolean(
-                        CarrierConfigManager.KEY_UNMETERED_NR_SA_MMWAVE_BOOL);
-                mNrSaSub6Unmetered = b.getBoolean(
-                        CarrierConfigManager.KEY_UNMETERED_NR_SA_SUB6_BOOL);
-                mNrNsaRoamingUnmetered = b.getBoolean(
-                        CarrierConfigManager.KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL);
-                mLteEndcUsingUserDataForRrcDetection = b.getBoolean(
-                        CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
-            }
-        }
-        updateLinkBandwidths(bandwidths, useLte);
-    }
-
-    public boolean getLteEndcUsingUserDataForIdleDetection() {
-        return mLteEndcUsingUserDataForRrcDetection;
-    }
-
-    /**
-     * Register for physical link status (i.e. RRC state) changed event.
-     * if {@link CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true,
-     * then physical link state is focusing on "internet data connection" instead of RRC state.
-     *
-     * @param h The handler
-     * @param what The event
-     */
-    public void registerForPhysicalLinkStatusChanged(Handler h, int what) {
-        mDcc.registerForPhysicalLinkStatusChanged(h, what);
-    }
-
-    /**
-     * Unregister from physical link status (i.e. RRC state) changed event.
-     *
-     * @param h The previously registered handler
-     */
-    public void unregisterForPhysicalLinkStatusChanged(Handler h) {
-        mDcc.unregisterForPhysicalLinkStatusChanged(h);
-    }
-
-    // We use a specialized equals function in Apn setting when checking if an active
-    // data connection is still legitimate to use against a different apn setting.
-    // This method is extracted to a function to ensure that any future changes to this check will
-    // be applied to both cleanUpConnectionsOnUpdatedApns and checkForCompatibleDataConnection.
-    // Fix for b/158908392.
-    private boolean areCompatible(ApnSetting apnSetting1, ApnSetting apnSetting2) {
-        return apnSetting1.equals(apnSetting2,
-                mPhone.getServiceState().getDataRoamingFromRegistration());
-    }
-
-    @NonNull
-    private PersistableBundle getCarrierConfig() {
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            // If an invalid subId is used, this bundle will contain default values.
-            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
-            if (config != null) {
-                return config;
-            }
-        }
-        // Return static default defined in CarrierConfigManager.
-        return CarrierConfigManager.getDefaultConfig();
-    }
-
-    /**
-     * @return The data service manager.
-     */
-    public @NonNull DataServiceManager getDataServiceManager() {
-        return mDataServiceManager;
-    }
-
-    /**
-     * @return The data throttler
-     */
-    public @NonNull DataThrottler getDataThrottler() {
-        return mDataThrottler;
-    }
-
-    private void showProvisioningNotification() {
-        final Intent intent = new Intent(DcTracker.INTENT_PROVISION);
-        intent.putExtra(DcTracker.EXTRA_PROVISION_PHONE_ID, mPhone.getPhoneId());
-        final PendingIntent pendingIntent = PendingIntent.getBroadcast(
-                mPhone.getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
-
-        final Resources r = mPhone.getContext().getResources();
-        final String title = r.getString(R.string.network_available_sign_in, 0);
-        final String details = mTelephonyManager.getNetworkOperator(mPhone.getSubId());
-        final Notification.Builder builder = new Notification.Builder(mPhone.getContext())
-                .setWhen(System.currentTimeMillis())
-                .setSmallIcon(R.drawable.stat_notify_rssi_in_range)
-                .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
-                .setAutoCancel(true)
-                .setTicker(title)
-                .setColor(mPhone.getContext().getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setContentTitle(title)
-                .setContentText(details)
-                .setContentIntent(pendingIntent)
-                .setLocalOnly(true)
-                .setOnlyAlertOnce(true);
-
-        final Notification notification = builder.build();
-        try {
-            getNotificationManager().notify(NOTIFICATION_TAG, mPhone.getPhoneId(), notification);
-        } catch (final NullPointerException npe) {
-            Log.e(mLogTag, "showProvisioningNotification: error showing notification", npe);
-        }
-    }
-
-    private void hideProvisioningNotification() {
-        try {
-            getNotificationManager().cancel(NOTIFICATION_TAG, mPhone.getPhoneId());
-        } catch (final NullPointerException npe) {
-            Log.e(mLogTag, "hideProvisioningNotification: error hiding notification", npe);
-        }
-    }
-
-    private NotificationManager getNotificationManager() {
-        return (NotificationManager) mPhone.getContext()
-                .createContextAsUser(UserHandle.ALL, 0 /* flags */)
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/README.txt b/src/java/com/android/internal/telephony/dataconnection/README.txt
deleted file mode 100644
index e613a00..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/README.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-This package contains classes used to manage a DataConnection.
-
-A criticial aspect of this class is that most objects in this
-package run on the same thread except DataConnectionTracker
-This makes processing efficient as it minimizes context
-switching and it eliminates issues with multi-threading.
-
-This can be done because all actions are either asynchronous
-or are known to be non-blocking and fast. At this time only
-DcTesterDeactivateAll takes specific advantage of this
-single threading knowledge by using Dcc#mDcListAll so be
-very careful when making changes that break this assumption.
-
-A related change was in DataConnectionAc I added code that
-checks to see if the caller is on a different thread. If
-it is then the AsyncChannel#sendMessageSynchronously is
-used. If the caller is on the same thread then a getter
-is used. This allows the DCAC to be used from any thread
-and was required to fix a bug when Dcc called
-PhoneBase#notifyDataConnection which calls DCT#getLinkProperties
-and DCT#getLinkCapabilities which call Dcc all on the same
-thread. Without this change there was a dead lock when
-sendMessageSynchronously blocks.
-
-
-== Testing ==
-
-The following are Intents that can be sent for testing pruproses on
-DEBUGGABLE builds (userdebug, eng)
-
-*) Causes bringUp and retry requests to fail for all DC's
-
-  adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_fail_bringup --ei counter 2 --ei fail_cause -3
-
-*) Causes all DC's to get torn down, simulating a temporary network outage:
-
-  adb shell am broadcast -a com.android.internal.telephony.dataconnection.action_deactivate_all
-
-*) To simplify testing we also have detach and attach simulations below where {x} is gsm, cdma or sip
-
-  adb shell am broadcast -a com.android.internal.telephony.{x}.action_detached
-  adb shell am broadcast -a com.android.internal.telephony.{x}.action_attached
-
-
-== System properties for Testing ==
-
-On debuggable builds (userdebug, eng) you can change additional
-settings through system properties.  These properties can be set with
-"setprop" for the current boot, or added to local.prop to persist
-across boots.
-
-device# setprop key value
-
-device# echo "key=value" >> /data/local.prop
-device# chmod 644 /data/local.prop
-
-
--- Retry configuration --
-
-You can replace the connection retry configuration.  For example, you
-could change it to perform 4 retries at 5 second intervals:
-
-device# setprop test.data_retry_config "5000,5000,5000"
-
-
--- Roaming --
-
-You can force the telephony stack to always assume that it's roaming
-to verify higher-level framework functionality:
-
-device# setprop telephony.test.forceRoaming true
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
deleted file mode 100644
index 63358f4..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.util.LocalLog;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.data.AccessNetworksManager;
-import com.android.internal.telephony.data.TelephonyNetworkFactory;
-import com.android.telephony.Rlog;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class represents the transport manager which manages available transports (i.e. WWAN or
- * WLAN) and determine the correct transport for {@link TelephonyNetworkFactory} to handle the data
- * requests.
- *
- * The device can operate in the following modes, which is stored in the system properties
- * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to
- * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode.
- *
- * Legacy mode:
- *      Frameworks send all data requests to the default data service, which is the cellular data
- *      service. IWLAN should be still reported as a RAT on cellular network service.
- *
- * AP-assisted mode:
- *      IWLAN is handled by IWLAN data service extending {@link android.telephony.data.DataService},
- *      IWLAN network service extending {@link android.telephony.NetworkService}, and qualified
- *      network service extending {@link android.telephony.data.QualifiedNetworksService}.
- *
- *      The following settings for service package name need to be configured properly for
- *      frameworks to bind.
- *
- *      Package name of data service:
- *          The resource overlay 'config_wlan_data_service_package' or,
- *          the carrier config
- *          {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
- *          The carrier config takes precedence over the resource overlay if both exist.
- *
- *      Package name of network service
- *          The resource overlay 'config_wlan_network_service_package' or
- *          the carrier config
- *          {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
- *          The carrier config takes precedence over the resource overlay if both exist.
- *
- *      Package name of qualified network service
- *          The resource overlay 'config_qualified_networks_service_package' or
- *          the carrier config
- *          {@link CarrierConfigManager#
- *          KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING}.
- *          The carrier config takes precedence over the resource overlay if both exist.
- */
-public class TransportManager extends Handler {
-    private final String mLogTag;
-
-    private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1;
-
-    private static final int EVENT_EVALUATE_TRANSPORT_PREFERENCE = 2;
-
-    // Delay the re-evaluation if transport fall back. QNS will need to quickly change the
-    // preference back to the original transport to avoid another handover request.
-    private static final long FALL_BACK_REEVALUATE_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(3);
-
-    private final Phone mPhone;
-
-    private final LocalLog mLocalLog = new LocalLog(64);
-
-    @Nullable
-    private AccessNetworksManager mAccessNetworksManager;
-
-    /**
-     * The pending handover list. This is a list of APNs that are being handover to the new
-     * transport. The entry will be removed once handover is completed. The key
-     * is the APN type, and the value is the target transport that the APN is handovered to.
-     */
-    private final SparseIntArray mPendingHandoverApns;
-
-    /**
-     * The registrants for listening data handover needed events.
-     */
-    private final RegistrantList mHandoverNeededEventRegistrants;
-
-    /**
-     * Handover parameters
-     */
-    @VisibleForTesting
-    public static final class HandoverParams {
-        /**
-         * The callback for handover complete.
-         */
-        public interface HandoverCallback {
-            /**
-             * Called when handover is completed.
-             *
-             * @param success {@true} if handover succeeded, otherwise failed.
-             * @param fallback {@true} if handover failed, the data connection fallback to the
-             * original transport
-             */
-            void onCompleted(boolean success, boolean fallback);
-        }
-
-        public final @ApnType int apnType;
-        public final int targetTransport;
-        public final HandoverCallback callback;
-
-        @VisibleForTesting
-        public HandoverParams(int apnType, int targetTransport, HandoverCallback callback) {
-            this.apnType = apnType;
-            this.targetTransport = targetTransport;
-            this.callback = callback;
-        }
-    }
-
-    public TransportManager(Phone phone) {
-        mPhone = phone;
-        mPendingHandoverApns = new SparseIntArray();
-        mHandoverNeededEventRegistrants = new RegistrantList();
-        mLogTag = TransportManager.class.getSimpleName() + "-" + mPhone.getPhoneId();
-        mAccessNetworksManager = mPhone.getAccessNetworksManager();
-        mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
-                EVENT_QUALIFIED_NETWORKS_CHANGED);
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case EVENT_QUALIFIED_NETWORKS_CHANGED:
-                if (!hasMessages(EVENT_EVALUATE_TRANSPORT_PREFERENCE)) {
-                    sendEmptyMessage(EVENT_EVALUATE_TRANSPORT_PREFERENCE);
-                }
-                break;
-            case EVENT_EVALUATE_TRANSPORT_PREFERENCE:
-                evaluateTransportPreference();
-                break;
-            default:
-                loge("Unexpected event " + msg.what);
-                break;
-        }
-    }
-
-    /**
-     * Set the current transport of apn type.
-     *
-     * @param apnType The APN type
-     * @param transport The transport. Must be WWAN or WLAN.
-     */
-    private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
-        mAccessNetworksManager.setCurrentTransport(apnType, transport);
-    }
-
-    private boolean isHandoverPending() {
-        return mPendingHandoverApns.size() > 0;
-    }
-
-    /**
-     * Evaluate the preferred transport for each APN type to see if handover is needed.
-     */
-    private void evaluateTransportPreference() {
-        // Simultaneously handover is not supported today. Preference will be re-evaluated after
-        // handover completed.
-        if (isHandoverPending()) return;
-        logl("evaluateTransportPreference");
-        for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
-            int targetTransport = mAccessNetworksManager.getPreferredTransport(apnType);
-            if (targetTransport != mAccessNetworksManager.getCurrentTransport(apnType)) {
-                logl("Handover started for APN type: "
-                        + ApnSetting.getApnTypeString(apnType)
-                        + ", target transport: "
-                        + AccessNetworkConstants.transportTypeToString(targetTransport));
-                mPendingHandoverApns.put(apnType, targetTransport);
-                mHandoverNeededEventRegistrants.notifyResult(
-                        new HandoverParams(apnType, targetTransport,
-                                (success, fallback) -> {
-                                    // The callback for handover completed.
-                                    if (success) {
-                                        logl("Handover succeeded for APN type "
-                                                + ApnSetting.getApnTypeString(apnType));
-                                    } else {
-                                        logl("APN type "
-                                                + ApnSetting.getApnTypeString(apnType)
-                                                + " handover to "
-                                                + AccessNetworkConstants.transportTypeToString(
-                                                targetTransport) + " failed"
-                                                + ", fallback=" + fallback);
-                                    }
-
-                                    long delay = 0;
-                                    if (fallback) {
-                                        // No need to change the preference because we should
-                                        // fallback. Re-evaluate after few seconds to give QNS
-                                        // some time to change the preference back to the original
-                                        // transport.
-                                        delay = FALL_BACK_REEVALUATE_DELAY_MILLIS;
-                                    } else {
-                                        // If handover succeeds or failed without falling back
-                                        // to the original transport, we should move to the new
-                                        // transport (even if it is failed).
-                                        setCurrentTransport(apnType, targetTransport);
-                                    }
-                                    mPendingHandoverApns.delete(apnType);
-                                    sendEmptyMessageDelayed(EVENT_EVALUATE_TRANSPORT_PREFERENCE,
-                                            delay);
-                                }));
-
-                // Return here instead of processing the next APN type. The next APN type for
-                // handover will be evaluate again once current handover is completed.
-                return;
-            }
-        }
-    }
-
-    /**
-     * Register for data handover needed event
-     *
-     * @param h The handler of the event
-     * @param what The id of the event
-     */
-    public void registerForHandoverNeededEvent(Handler h, int what) {
-        if (h != null) {
-            mHandoverNeededEventRegistrants.addUnique(h, what, null);
-        }
-    }
-
-    /**
-     * Unregister for data handover needed event
-     *
-     * @param h The handler
-     */
-    public void unregisterForHandoverNeededEvent(Handler h) {
-        mHandoverNeededEventRegistrants.remove(h);
-    }
-
-    /**
-     * Registers the data throttler with DcTracker.
-     */
-    public void registerDataThrottler(DataThrottler dataThrottler) {
-        if (mAccessNetworksManager != null) {
-            mAccessNetworksManager.registerDataThrottler(dataThrottler);
-        }
-    }
-
-    private void logl(String s) {
-        log(s);
-        mLocalLog.log(s);
-    }
-
-    private void log(String s) {
-        Rlog.d(mLogTag, s);
-    }
-
-    private void loge(String s) {
-        Rlog.e(mLogTag, s);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java
new file mode 100644
index 0000000..6caf5ab
--- /dev/null
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyConstants.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.emergency;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Define the constants for emergency call domain selection.
+ */
+public class EmergencyConstants {
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"MODE_EMERGENCY_"},
+            value = {
+                    MODE_EMERGENCY_NONE,
+                    MODE_EMERGENCY_WWAN,
+                    MODE_EMERGENCY_WLAN,
+                    MODE_EMERGENCY_CALLBACK,
+            })
+    public @interface EmergencyMode {}
+
+    /**
+     * Default value.
+     */
+    public static final int MODE_EMERGENCY_NONE = 0;
+    /**
+     * Mode Type Emergency WWAN, indicates that the current domain selected for the Emergency call
+     * is cellular.
+     */
+    public static final int MODE_EMERGENCY_WWAN = 1;
+    /**
+     * Mode Type Emergency WLAN, indicates that the current domain selected for the Emergency call
+     * is WLAN/WIFI.
+     */
+    public static final int MODE_EMERGENCY_WLAN = 2;
+    /**
+     * Mode Type Emergency Callback, indicates that the current mode set request is for Emergency
+     * callback.
+     */
+    public static final int MODE_EMERGENCY_CALLBACK = 3;
+
+    /** Converts the {@link EmergencyMode} to String */
+    public static String emergencyModeToString(int emcMode) {
+        switch (emcMode) {
+            case MODE_EMERGENCY_NONE: return "NONE";
+            case MODE_EMERGENCY_WWAN: return "WWAN";
+            case MODE_EMERGENCY_WLAN: return "WLAN";
+            case MODE_EMERGENCY_CALLBACK: return "CALLBACK";
+            default: return "UNKNOWN(" + emcMode + ")";
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 276d82a..af40f2c 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -16,10 +16,14 @@
 
 package com.android.internal.telephony.emergency;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Environment;
 import android.os.Handler;
@@ -28,6 +32,7 @@
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -36,6 +41,8 @@
 import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LocalLog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +74,9 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -95,9 +105,18 @@
 
     private final CommandsInterface mCi;
     private final Phone mPhone;
+    private int mPhoneId;
     private String mCountryIso;
     private String mLastKnownEmergencyCountryIso = "";
     private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
+    private boolean mIsHalVersionLessThan1Dot4 = false;
+    private Resources mResources = null;
+    /**
+     * Used for storing all specific mnc's along with the list of emergency numbers
+     * for which normal routing should be supported.
+     */
+    private Map<String, Set<String>> mNormalRoutedNumbers = new ArrayMap<>();
+
     /**
      * Indicates if the country iso is set by another subscription.
      * @hide
@@ -164,8 +183,10 @@
     public EmergencyNumberTracker(Phone phone, CommandsInterface ci) {
         mPhone = phone;
         mCi = ci;
+        mResources = mPhone.getContext().getResources();
 
         if (mPhone != null) {
+            mPhoneId = phone.getPhoneId();
             CarrierConfigManager configMgr = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
             if (configMgr != null) {
@@ -185,6 +206,9 @@
             filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
 
             mPhone.getContext().registerReceiver(mIntentReceiver, filter);
+
+            mIsHalVersionLessThan1Dot4 = mPhone.getHalVersion(HAL_SERVICE_VOICE)
+                                                 .lessOrEqual(new HalVersion(1, 3));
         } else {
             loge("mPhone is null.");
         }
@@ -284,7 +308,7 @@
         // If country iso has been cached when listener is set, don't need to cache the initial
         // country iso and initial database.
         if (mCountryIso == null) {
-            String countryForDatabaseCache = getInitialCountryIso().toLowerCase();
+            String countryForDatabaseCache = getInitialCountryIso().toLowerCase(Locale.ROOT);
             updateEmergencyCountryIso(countryForDatabaseCache);
             // Use the last known country to cache the database in APM
             if (TextUtils.isEmpty(countryForDatabaseCache)
@@ -404,7 +428,8 @@
                 EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, null).sendToTarget();
     }
 
-    private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso) {
+    private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso,
+            int emergencyCallRouting) {
         String phoneNumber = eccInfo.phoneNumber.trim();
         if (phoneNumber.isEmpty()) {
             loge("EccInfo has empty phone number.");
@@ -445,13 +470,65 @@
                     // Ignores unknown types.
             }
         }
-        return new EmergencyNumber(phoneNumber, countryIso, "", emergencyServiceCategoryBitmask,
-                new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        return new EmergencyNumber(phoneNumber, countryIso, "",
+                emergencyServiceCategoryBitmask, new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE, emergencyCallRouting);
+    }
+
+    /**
+     * Get routing type of emergency numbers from DB. Update mnc's list with numbers that are
+     * to supported as normal routing type in the respective mnc's.
+     */
+    private int getRoutingInfoFromDB(EccInfo eccInfo,
+            Map<String, Set<String>> normalRoutedNumbers) {
+        int emergencyCallRouting;
+        switch(eccInfo.routing)
+        {
+            case EccInfo.Routing.NORMAL :
+                emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+                break;
+            case EccInfo.Routing.EMERGENCY :
+                emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY;
+                break;
+            default:
+                emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+        }
+        String phoneNumber = eccInfo.phoneNumber.trim();
+        if (phoneNumber.isEmpty()) {
+            loge("EccInfo has empty phone number.");
+            return emergencyCallRouting;
+        }
+
+        if (eccInfo.routing == EccInfo.Routing.NORMAL) {
+            emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+
+            if (((eccInfo.normalRoutingMncs).length != 0)
+                    && (eccInfo.normalRoutingMncs[0].length() > 0)) {
+                emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY;
+
+                for (String routingMnc : eccInfo.normalRoutingMncs) {
+                    boolean mncExist = normalRoutedNumbers.containsKey(routingMnc);
+                    Set phoneNumberList;
+                    if (!mncExist) {
+                        phoneNumberList = new ArraySet<String>();
+                        phoneNumberList.add(phoneNumber);
+                        normalRoutedNumbers.put(routingMnc, phoneNumberList);
+                    } else {
+                        phoneNumberList = normalRoutedNumbers.get(routingMnc);
+                        if (!phoneNumberList.contains(phoneNumber)) {
+                            phoneNumberList.add(phoneNumber);
+                        }
+                    }
+                }
+                logd("Normal routed mncs with phoneNumbers:" + normalRoutedNumbers);
+            }
+        }
+        return emergencyCallRouting;
     }
 
     private void cacheEmergencyDatabaseByCountry(String countryIso) {
         int assetsDatabaseVersion;
+        Map<String, Set<String>> assetNormalRoutedNumbers = new ArrayMap<>();
 
         // Read the Asset emergency number database
         List<EmergencyNumber> updatedAssetEmergencyNumberList = new ArrayList<>();
@@ -465,10 +542,15 @@
             logd(countryIso + " asset emergency database is loaded. Ver: " + assetsDatabaseVersion
                     + " Phone Id: " + mPhone.getPhoneId());
             for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
-                if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
+                if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) {
                     for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
+                        int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+                        if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) {
+                            emergencyCallRouting = getRoutingInfoFromDB(eccInfo,
+                                    assetNormalRoutedNumbers);
+                        }
                         updatedAssetEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
-                                eccInfo, countryIso));
+                                eccInfo, countryIso, emergencyCallRouting));
                     }
                 }
             }
@@ -489,6 +571,8 @@
             logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion);
             mCurrentDatabaseVersion = assetsDatabaseVersion;
             mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList;
+            mNormalRoutedNumbers.clear();
+            mNormalRoutedNumbers = assetNormalRoutedNumbers;
         } else {
             logd("Using Ota Emergency database. Version: " + otaDatabaseVersion);
         }
@@ -497,6 +581,7 @@
     private int cacheOtaEmergencyNumberDatabase() {
         ProtobufEccData.AllInfo allEccMessages = null;
         int otaDatabaseVersion = INVALID_DATABASE_VERSION;
+        Map<String, Set<String>> otaNormalRoutedNumbers = new ArrayMap<>();
 
         // Read the OTA emergency number database
         List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>();
@@ -525,10 +610,15 @@
             logd(countryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion);
             otaDatabaseVersion = allEccMessages.revision;
             for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
-                if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
+                if (countryEccInfo.isoCode.equals(countryIso.toUpperCase(Locale.ROOT))) {
                     for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
+                        int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+                        if (!shouldEmergencyNumberRoutingFromDbBeIgnored()) {
+                            emergencyCallRouting = getRoutingInfoFromDB(eccInfo,
+                                    otaNormalRoutedNumbers);
+                        }
                         updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
-                                eccInfo, countryIso));
+                                eccInfo, countryIso, emergencyCallRouting));
                     }
                 }
             }
@@ -543,6 +633,8 @@
                 && mCurrentDatabaseVersion < otaDatabaseVersion) {
             mCurrentDatabaseVersion = otaDatabaseVersion;
             mEmergencyNumberListFromDatabase = updatedOtaEmergencyNumberList;
+            mNormalRoutedNumbers.clear();
+            mNormalRoutedNumbers = otaNormalRoutedNumbers;
         }
         return otaDatabaseVersion;
     }
@@ -591,7 +683,7 @@
     private void updateEmergencyNumberListDatabaseAndNotify(String countryIso) {
         logd("updateEmergencyNumberListDatabaseAndNotify(): receiving countryIso: "
                 + countryIso);
-        updateEmergencyCountryIso(countryIso.toLowerCase());
+        updateEmergencyCountryIso(countryIso.toLowerCase(Locale.ROOT));
         // Use cached country iso in APM to load emergency number database.
         if (TextUtils.isEmpty(countryIso) && isAirplaneModeEnabled()) {
             countryIso = getCountryIsoForCachingDatabase();
@@ -698,11 +790,96 @@
      *         indication not support from the HAL.
      */
     public List<EmergencyNumber> getEmergencyNumberList() {
+        List<EmergencyNumber> completeEmergencyNumberList;
         if (!mEmergencyNumberListFromRadio.isEmpty()) {
-            return Collections.unmodifiableList(mEmergencyNumberList);
+            completeEmergencyNumberList = Collections.unmodifiableList(mEmergencyNumberList);
         } else {
-            return getEmergencyNumberListFromEccListDatabaseAndTest();
+            completeEmergencyNumberList = getEmergencyNumberListFromEccListDatabaseAndTest();
         }
+        if (shouldAdjustForRouting()) {
+            return adjustRoutingForEmergencyNumbers(completeEmergencyNumberList);
+        } else {
+            return completeEmergencyNumberList;
+        }
+    }
+
+    /**
+     * Util function to check whether routing type and mnc value in emergency number needs
+     * to be adjusted for the current network mnc.
+     */
+    private boolean shouldAdjustForRouting() {
+        if (!shouldEmergencyNumberRoutingFromDbBeIgnored() && !mNormalRoutedNumbers.isEmpty()) {
+            CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+            if (cellIdentity != null) {
+                String networkMnc = cellIdentity.getMncString();
+                if (mNormalRoutedNumbers.containsKey(networkMnc)) {
+                    Set<String> phoneNumbers = mNormalRoutedNumbers.get(networkMnc);
+                    if (phoneNumbers != null && !phoneNumbers.isEmpty()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Adjust emergency numbers with mnc and routing type based on the current network mnc.
+     */
+    private List<EmergencyNumber> adjustRoutingForEmergencyNumbers(
+            List<EmergencyNumber> emergencyNumbers) {
+        CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+        if (cellIdentity != null) {
+            String networkMnc = cellIdentity.getMncString();
+
+            Set<String> normalRoutedPhoneNumbers = mNormalRoutedNumbers.get(networkMnc);
+            if (normalRoutedPhoneNumbers == null || normalRoutedPhoneNumbers.isEmpty()) {
+                return emergencyNumbers;
+            }
+            Set<String> normalRoutedPhoneNumbersWithPrefix = new ArraySet<String>();
+            for (String num : normalRoutedPhoneNumbers) {
+                Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num);
+                if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) {
+                    normalRoutedPhoneNumbersWithPrefix.addAll(phoneNumbersWithPrefix);
+                }
+            }
+            List<EmergencyNumber> adjustedEmergencyNumberList = new ArrayList<>();
+            int routing;
+            String mnc;
+            for (EmergencyNumber num : emergencyNumbers) {
+                routing = num.getEmergencyCallRouting();
+                mnc = num.getMnc();
+                if (num.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)
+                        && (normalRoutedPhoneNumbers.contains(num.getNumber())
+                        || (normalRoutedPhoneNumbersWithPrefix.contains(num.getNumber())))) {
+                    routing = EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
+                    mnc = networkMnc;
+                    logd("adjustRoutingForEmergencyNumbers for number" + num.getNumber());
+                }
+                adjustedEmergencyNumberList.add(new EmergencyNumber(num.getNumber(),
+                        num.getCountryIso(), mnc,
+                        num.getEmergencyServiceCategoryBitmask(),
+                        num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
+                        routing));
+            }
+            return adjustedEmergencyNumberList;
+        } else {
+            return emergencyNumbers;
+        }
+    }
+
+
+    /**
+     * Util function to add prefix to the given emergency number.
+     */
+    private Set<String> addPrefixToEmergencyNumber(String number) {
+        Set<String> phoneNumbersWithPrefix = new ArraySet<String>();
+        for (String prefix : mEmergencyNumberPrefix) {
+            if (!number.startsWith(prefix)) {
+                phoneNumbersWithPrefix.add(prefix + number);
+            }
+        }
+        return phoneNumbersWithPrefix;
     }
 
     /**
@@ -710,7 +887,7 @@
      *
      * @return {@code true} if it is; {@code false} otherwise.
      */
-    public boolean isEmergencyNumber(String number, boolean exactMatch) {
+    public boolean isEmergencyNumber(String number) {
         if (number == null) {
             return false;
         }
@@ -726,31 +903,14 @@
 
         if (!mEmergencyNumberListFromRadio.isEmpty()) {
             for (EmergencyNumber num : mEmergencyNumberList) {
-                // According to com.android.i18n.phonenumbers.ShortNumberInfo, in
-                // these countries, if extra digits are added to an emergency number,
-                // it no longer connects to the emergency service.
-                String countryIso = getLastKnownEmergencyCountryIso();
-                if (countryIso.equals("br") || countryIso.equals("cl")
-                        || countryIso.equals("ni")) {
-                    exactMatch = true;
-                } else {
-                    exactMatch = false || exactMatch;
-                }
-                if (exactMatch) {
-                    if (num.getNumber().equals(number)) {
-                        logd("Found in mEmergencyNumberList [exact match] ");
-                        return true;
-                    }
-                } else {
-                    if (number.startsWith(num.getNumber())) {
-                        logd("Found in mEmergencyNumberList [not exact match] ");
-                        return true;
-                    }
+                if (num.getNumber().equals(number)) {
+                    logd("Found in mEmergencyNumberList");
+                    return true;
                 }
             }
             return false;
         } else {
-            boolean inEccList = isEmergencyNumberFromEccList(number, exactMatch);
+            boolean inEccList = isEmergencyNumberFromEccList(number);
             boolean inEmergencyNumberDb = isEmergencyNumberFromDatabase(number);
             boolean inEmergencyNumberTestList = isEmergencyNumberForTest(number);
             logd("Search results - inRilEccList:" + inEccList
@@ -855,22 +1015,11 @@
      */
     private List<EmergencyNumber> getEmergencyNumberListFromEccList() {
         List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
-        int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
 
-        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-        String emergencyNumbers = SystemProperties.get(ecclist, "");
-        if (TextUtils.isEmpty(emergencyNumbers)) {
-            // then read-only ecclist property since old RIL only uses this
-            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+        if (mIsHalVersionLessThan1Dot4) {
+            emergencyNumberList.addAll(getEmergencyNumberListFromEccListForHalv1_3());
         }
-        if (!TextUtils.isEmpty(emergencyNumbers)) {
-            // searches through the comma-separated list for a match,
-            // return true if one is found.
-            for (String emergencyNum : emergencyNumbers.split(",")) {
-                emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum));
-            }
-        }
-        emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
+        String emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
         for (String emergencyNum : emergencyNumbers.split(",")) {
             emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum));
         }
@@ -881,20 +1030,45 @@
         return emergencyNumberList;
     }
 
+    private String getEmergencyNumberListForHalv1_3() {
+        int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
+
+        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+        String emergencyNumbers = SystemProperties.get(ecclist, "");
+
+        if (TextUtils.isEmpty(emergencyNumbers)) {
+            // then read-only ecclist property since old RIL only uses this
+            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+        }
+        logd(ecclist + " emergencyNumbers: " + emergencyNumbers);
+        return emergencyNumbers;
+    }
+
+    private List<EmergencyNumber> getEmergencyNumberListFromEccListForHalv1_3() {
+        List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
+        String emergencyNumbers = getEmergencyNumberListForHalv1_3();
+
+        if (!TextUtils.isEmpty(emergencyNumbers)) {
+            for (String emergencyNum : emergencyNumbers.split(",")) {
+                emergencyNumberList.add(getLabeledEmergencyNumberForEcclist(emergencyNum));
+            }
+        }
+        return emergencyNumberList;
+    }
+
     private List<EmergencyNumber> getEmergencyNumberListWithPrefix(
             List<EmergencyNumber> emergencyNumberList) {
         List<EmergencyNumber> emergencyNumberListWithPrefix = new ArrayList<>();
         if (emergencyNumberList != null) {
             for (EmergencyNumber num : emergencyNumberList) {
-                for (String prefix : mEmergencyNumberPrefix) {
-                    // If an emergency number has started with the prefix,
-                    // no need to apply the prefix.
-                    if (!num.getNumber().startsWith(prefix)) {
+                Set<String> phoneNumbersWithPrefix = addPrefixToEmergencyNumber(num.getNumber());
+                if (phoneNumbersWithPrefix != null && !phoneNumbersWithPrefix.isEmpty()) {
+                    for (String numberWithPrefix : phoneNumbersWithPrefix) {
                         emergencyNumberListWithPrefix.add(new EmergencyNumber(
-                            prefix + num.getNumber(), num.getCountryIso(),
-                            num.getMnc(), num.getEmergencyServiceCategoryBitmask(),
-                            num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
-                            num.getEmergencyCallRouting()));
+                                numberWithPrefix, num.getCountryIso(),
+                                num.getMnc(), num.getEmergencyServiceCategoryBitmask(),
+                                num.getEmergencyUrns(), num.getEmergencyNumberSourceBitmask(),
+                                num.getEmergencyCallRouting()));
                     }
                 }
             }
@@ -913,7 +1087,7 @@
     }
 
     private boolean isEmergencyNumberFromDatabase(String number) {
-        if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
+        if (mEmergencyNumberListFromDatabase.isEmpty()) {
             return false;
         }
         number = PhoneNumberUtils.stripSeparators(number);
@@ -936,10 +1110,10 @@
         number = PhoneNumberUtils.stripSeparators(number);
         for (EmergencyNumber num : mEmergencyNumberListFromDatabase) {
             if (num.getNumber().equals(number)) {
-                return new EmergencyNumber(number, getLastKnownEmergencyCountryIso().toLowerCase(),
-                        "", num.getEmergencyServiceCategoryBitmask(),
+                return new EmergencyNumber(number, getLastKnownEmergencyCountryIso()
+                        .toLowerCase(Locale.ROOT), "", num.getEmergencyServiceCategoryBitmask(),
                         new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                        EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+                        num.getEmergencyCallRouting());
             }
         }
         return new EmergencyNumber(number, "", "",
@@ -952,7 +1126,7 @@
      * Back-up old logics for {@link PhoneNumberUtils#isEmergencyNumberInternal} for legacy
      * and deprecate purpose.
      */
-    private boolean isEmergencyNumberFromEccList(String number, boolean useExactMatch) {
+    private boolean isEmergencyNumberFromEccList(String number) {
         // If the number passed in is null, just return false:
         if (number == null) return false;
 
@@ -976,57 +1150,14 @@
         /// @}
 
         String emergencyNumbers = "";
-        int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
-
-        String ecclist = null;
         String countryIso = getLastKnownEmergencyCountryIso();
+        logd("country:" + countryIso);
 
-        if (!mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
-            //only use ril ecc list for older devices with HAL < 1.4
-            // check read-write ecclist property first
-            ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-            emergencyNumbers = SystemProperties.get(ecclist, "");
-
-            logd("slotId:" + slotId + " country:" + countryIso + " emergencyNumbers: "
-                + emergencyNumbers);
-
-            if (TextUtils.isEmpty(emergencyNumbers)) {
-                // then read-only ecclist property since old RIL only uses this
-                emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
-            }
+        if (mIsHalVersionLessThan1Dot4) {
+            emergencyNumbers = getEmergencyNumberListForHalv1_3();
 
             if (!TextUtils.isEmpty(emergencyNumbers)) {
-                // searches through the comma-separated list for a match,
-                // return true if one is found.
-                for (String emergencyNum : emergencyNumbers.split(",")) {
-                    // According to com.android.i18n.phonenumbers.ShortNumberInfo, in
-                    // these countries, if extra digits are added to an emergency number,
-                    // it no longer connects to the emergency service.
-                    if (useExactMatch || countryIso.equals("br") || countryIso.equals("cl")
-                        || countryIso.equals("ni")) {
-                        if (number.equals(emergencyNum)) {
-                            return true;
-                        } else {
-                            for (String prefix : mEmergencyNumberPrefix) {
-                                if (number.equals(prefix + emergencyNum)) {
-                                    return true;
-                                }
-                            }
-                        }
-                    } else {
-                        if (number.startsWith(emergencyNum)) {
-                            return true;
-                        } else {
-                            for (String prefix : mEmergencyNumberPrefix) {
-                                if (number.startsWith(prefix + emergencyNum)) {
-                                    return true;
-                                }
-                            }
-                        }
-                    }
-                }
-                // no matches found against the list!
-                return false;
+                return isEmergencyNumberFromEccListForHalv1_3(number, emergencyNumbers);
             }
         }
 
@@ -1038,63 +1169,57 @@
         emergencyNumbers = ((isSimAbsent()) ? "112,911,000,08,110,118,119,999" : "112,911");
 
         for (String emergencyNum : emergencyNumbers.split(",")) {
-            if (useExactMatch) {
-                if (number.equals(emergencyNum)) {
-                    return true;
-                } else {
-                    for (String prefix : mEmergencyNumberPrefix) {
-                        if (number.equals(prefix + emergencyNum)) {
-                            return true;
-                        }
-                    }
-                }
+            if (number.equals(emergencyNum)) {
+                return true;
             } else {
-                if (number.startsWith(emergencyNum)) {
-                    return true;
-                } else {
-                    for (String prefix : mEmergencyNumberPrefix) {
-                        if (number.equals(prefix + emergencyNum)) {
-                            return true;
-                        }
+                for (String prefix : mEmergencyNumberPrefix) {
+                    if (number.equals(prefix + emergencyNum)) {
+                        return true;
                     }
                 }
             }
         }
 
-        if(isSimAbsent()) {
+        if (isSimAbsent()) {
             // No ecclist system property, so use our own list.
             if (countryIso != null) {
                 ShortNumberInfo info = ShortNumberInfo.getInstance();
-                if (useExactMatch) {
-                    if (info.isEmergencyNumber(number, countryIso.toUpperCase())) {
-                        return true;
-                    } else {
-                        for (String prefix : mEmergencyNumberPrefix) {
-                            if (info.isEmergencyNumber(prefix + number, countryIso.toUpperCase())) {
-                                return true;
-                            }
-                        }
-                    }
-                    return false;
+                if (info.isEmergencyNumber(number, countryIso.toUpperCase(Locale.ROOT))) {
+                    return true;
                 } else {
-                    if (info.connectsToEmergencyNumber(number, countryIso.toUpperCase())) {
-                        return true;
-                    } else {
-                        for (String prefix : mEmergencyNumberPrefix) {
-                            if (info.connectsToEmergencyNumber(prefix + number,
-                                    countryIso.toUpperCase())) {
-                                return true;
-                            }
+                    for (String prefix : mEmergencyNumberPrefix) {
+                        if (info.isEmergencyNumber(prefix + number,
+                                countryIso.toUpperCase(Locale.ROOT))) {
+                            return true;
                         }
                     }
-                    return false;
                 }
+                return false;
             }
         }
 
         return false;
     }
 
+    private boolean isEmergencyNumberFromEccListForHalv1_3(@NonNull String number,
+            @NonNull String emergencyNumbers) {
+        // searches through the comma-separated list for a match,
+        // return true if one is found.
+        for (String emergencyNum : emergencyNumbers.split(",")) {
+            if (number.equals(emergencyNum)) {
+                return true;
+            } else {
+                for (String prefix : mEmergencyNumberPrefix) {
+                    if (number.equals(prefix + emergencyNum)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        // no matches found against the list!
+        return false;
+    }
+
     /**
      * Execute command for updating emergency number for test mode.
      */
@@ -1107,7 +1232,7 @@
      */
     private void updateEmergencyNumberListTestModeAndNotify(int action, EmergencyNumber num) {
         if (action == ADD_EMERGENCY_NUMBER_TEST_MODE) {
-            if (!isEmergencyNumber(num.getNumber(), true)) {
+            if (!isEmergencyNumber(num.getNumber())) {
                 mEmergencyNumberListFromTestMode.add(num);
             }
         } else if (action == RESET_EMERGENCY_NUMBER_TEST_MODE) {
@@ -1134,7 +1259,7 @@
 
     private List<EmergencyNumber> getEmergencyNumberListFromEccListDatabaseAndTest() {
         List<EmergencyNumber> mergedEmergencyNumberList = getEmergencyNumberListFromEccList();
-        if (mPhone.getHalVersion().greaterOrEqual(new HalVersion(1, 4))) {
+        if (!mEmergencyNumberListFromDatabase.isEmpty()) {
             loge("getEmergencyNumberListFromEccListDatabaseAndTest: radio indication is"
                     + " unavailable in 1.4 HAL.");
             mergedEmergencyNumberList.addAll(mEmergencyNumberListFromDatabase);
@@ -1158,16 +1283,16 @@
         return new ArrayList<>(mEmergencyNumberListFromRadio);
     }
 
-    private static void logd(String str) {
-        Rlog.d(TAG, str);
+    private void logd(String str) {
+        Rlog.d(TAG, "[" + mPhoneId + "]" + str);
     }
 
-    private static void logw(String str) {
-        Rlog.w(TAG, str);
+    private void logw(String str) {
+        Rlog.w(TAG, "[" + mPhoneId + "]" + str);
     }
 
-    private static void loge(String str) {
-        Rlog.e(TAG, str);
+    private void loge(String str) {
+        Rlog.e(TAG, "[" + mPhoneId + "]" +  str);
     }
 
     private void writeUpdatedEmergencyNumberListMetrics(
@@ -1182,6 +1307,28 @@
     }
 
     /**
+     * @return {@code true} if emergency numbers sourced from modem/config should be ignored.
+     * {@code false} if emergency numbers sourced from modem/config should not be ignored.
+     */
+    @VisibleForTesting
+    public boolean shouldModemConfigEmergencyNumbersBeIgnored() {
+        return mResources.getBoolean(com.android.internal.R.bool
+                .ignore_modem_config_emergency_numbers);
+    }
+
+    /**
+     * @return {@code true} if emergency number routing from the android emergency number
+     * database should be ignored.
+     * {@code false} if emergency number routing from the android emergency number database
+     * should not be ignored.
+     */
+    @VisibleForTesting
+    public boolean shouldEmergencyNumberRoutingFromDbBeIgnored() {
+        return mResources.getBoolean(com.android.internal.R.bool
+                .ignore_emergency_number_routing_from_db);
+    }
+
+    /**
      * Dump Emergency Number List info in the tracking
      *
      * @param fd FileDescriptor
@@ -1190,7 +1337,7 @@
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-        ipw.println(" Hal Version:" + mPhone.getHalVersion());
+        ipw.println(" Hal Version:" + mPhone.getHalVersion(HAL_SERVICE_VOICE));
         ipw.println(" ========================================= ");
 
         ipw.println(" Country Iso:" + getEmergencyCountryIso());
@@ -1229,10 +1376,10 @@
         ipw.decreaseIndent();
         ipw.println(" ========================================= ");
 
-        int slotId = SubscriptionController.getInstance().getSlotIndex(mPhone.getSubId());
-        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-        ipw.println(" ril.ecclist: " + SystemProperties.get(ecclist, ""));
-        ipw.println(" ========================================= ");
+        if (mIsHalVersionLessThan1Dot4) {
+            getEmergencyNumberListForHalv1_3();
+            ipw.println(" ========================================= ");
+        }
 
         ipw.println("Emergency Number List for Phone" + "(" + mPhone.getPhoneId() + ")");
         ipw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index f337141..6a0011d 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -413,7 +413,7 @@
         // if there is no iccid enabled on this port, return null.
         if (TextUtils.isEmpty(iccId)) {
             try {
-                callback.onComplete(EuiccCardManager.RESULT_PROFILE_NOT_FOUND, null);
+                callback.onComplete(EuiccCardManager.RESULT_PROFILE_DOES_NOT_EXIST, null);
             } catch (RemoteException exception) {
                 loge("getEnabledProfile callback failure.", exception);
             }
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 974acf9..96a9328 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -389,6 +389,9 @@
         mSm = (SubscriptionManager)
                 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
 
+        // TODO(b/239277548): Disable debug logging after analysing this bug.
+        setDbg(true);
+
         // Unavailable/Available both monitor for package changes and update mSelectedComponent but
         // do not need to adjust the binding.
         mUnavailableState = new UnavailableState();
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 0abd4ab..b1f62fa 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -43,7 +43,7 @@
  */
 public class GsmInboundSmsHandler extends InboundSmsHandler {
 
-    private static BroadcastReceiver sTestBroadcastReceiver;
+    private BroadcastReceiver mTestBroadcastReceiver;
     /** Handler for SMS-PP data download messages to UICC. */
     private final UsimDataDownloadHandler mDataDownloadHandler;
 
@@ -63,11 +63,11 @@
         mCellBroadcastServiceManager.enable();
 
         if (TEST_MODE) {
-            if (sTestBroadcastReceiver == null) {
-                sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
+            if (mTestBroadcastReceiver == null) {
+                mTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
                 IntentFilter filter = new IntentFilter();
                 filter.addAction(TEST_ACTION);
-                context.registerReceiver(sTestBroadcastReceiver, filter,
+                context.registerReceiver(mTestBroadcastReceiver, filter,
                         Context.RECEIVER_EXPORTED);
             }
         }
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index dec2468..114a7d8 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -49,6 +49,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallWaitingController;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.GsmCdmaPhone;
@@ -61,6 +62,7 @@
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.telephony.Rlog;
 
+import java.util.ArrayList;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -455,7 +457,7 @@
         return "";
     }
 
-    private String getActionStringFromReqType(SsData.RequestType rType) {
+    private static String getActionStringFromReqType(SsData.RequestType rType) {
         switch (rType) {
             case SS_ACTIVATION:
                 return ACTION_ACTIVATE;
@@ -520,6 +522,40 @@
         }
     }
 
+    public static SsData.ServiceType cfReasonToServiceType(int commandInterfaceCFReason) {
+        switch (commandInterfaceCFReason) {
+            case CommandsInterface.CF_REASON_UNCONDITIONAL:
+                return  SsData.ServiceType.SS_CFU;
+            case CommandsInterface.CF_REASON_BUSY:
+                return SsData.ServiceType.SS_CF_BUSY;
+            case CommandsInterface.CF_REASON_NO_REPLY:
+                return SsData.ServiceType.SS_CF_NO_REPLY;
+            case CommandsInterface.CF_REASON_NOT_REACHABLE:
+                return SsData.ServiceType.SS_CF_NOT_REACHABLE;
+            case CommandsInterface.CF_REASON_ALL:
+                return SsData.ServiceType.SS_CF_ALL;
+            case CommandsInterface.CF_REASON_ALL_CONDITIONAL:
+                return SsData.ServiceType.SS_CF_ALL_CONDITIONAL;
+            default:
+                return null;
+        }
+    }
+
+    public static SsData.RequestType cfActionToRequestType(int commandInterfaceCFAction) {
+        switch (commandInterfaceCFAction) {
+            case CommandsInterface.CF_ACTION_DISABLE:
+                return SsData.RequestType.SS_DEACTIVATION;
+            case CommandsInterface.CF_ACTION_ENABLE:
+                return SsData.RequestType.SS_ACTIVATION;
+            case CommandsInterface.CF_ACTION_REGISTRATION:
+                return SsData.RequestType.SS_REGISTRATION;
+            case CommandsInterface.CF_ACTION_ERASURE:
+                return SsData.RequestType.SS_ERASURE;
+            default:
+                return null;
+        }
+    }
+
     @UnsupportedAppUsage
     private static int
     siToServiceClass(String si) {
@@ -623,6 +659,29 @@
         }
     }
 
+    public static SsData.ServiceType cbFacilityToServiceType(String commandInterfaceCBFacility) {
+        switch(commandInterfaceCBFacility) {
+            case CommandsInterface.CB_FACILITY_BAOC:
+                return SsData.ServiceType.SS_BAOC;
+            case CommandsInterface.CB_FACILITY_BAOIC:
+                return SsData.ServiceType.SS_BAOIC;
+            case CommandsInterface.CB_FACILITY_BAOICxH:
+                return SsData.ServiceType.SS_BAOIC_EXC_HOME;
+            case CommandsInterface.CB_FACILITY_BAIC:
+                return SsData.ServiceType.SS_BAIC;
+            case CommandsInterface.CB_FACILITY_BAICr:
+                return SsData.ServiceType.SS_BAIC_ROAMING;
+            case CommandsInterface.CB_FACILITY_BA_ALL:
+                return SsData.ServiceType.SS_ALL_BARRING;
+            case CommandsInterface.CB_FACILITY_BA_MO:
+                return SsData.ServiceType.SS_OUTGOING_BARRING;
+            case CommandsInterface.CB_FACILITY_BA_MT:
+                return SsData.ServiceType.SS_INCOMING_BARRING;
+            default:
+                return null;
+        }
+    }
+
     //***** Constructor
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -892,6 +951,17 @@
         return CommandsInterface.CLIR_DEFAULT;
     }
 
+    public static SsData.RequestType clirModeToRequestType(int commandInterfaceCLIRMode) {
+        switch (commandInterfaceCLIRMode) {
+            case CommandsInterface.CLIR_SUPPRESSION:
+                return SsData.RequestType.SS_ACTIVATION;
+            case CommandsInterface.CLIR_INVOCATION:
+                return SsData.RequestType.SS_DEACTIVATION;
+            default:
+                return null;
+        }
+    }
+
     /**
      * Returns true if the Service Code is FAC to dial as a normal call.
      *
@@ -979,6 +1049,9 @@
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             } else if (mSc != null && mSc.equals(SC_CLIP)) {
                 Rlog.d(LOG_TAG, "processCode: is CLIP");
+                if (!mPhone.supportCsfbForSs()) {
+                    throw new RuntimeException("No network to support supplementary services");
+                }
                 if (isInterrogate()) {
                     mPhone.mCi.queryCLIP(
                             obtainMessage(EVENT_QUERY_COMPLETE, this));
@@ -987,6 +1060,9 @@
                 }
             } else if (mSc != null && mSc.equals(SC_CLIR)) {
                 Rlog.d(LOG_TAG, "processCode: is CLIR");
+                if (!mPhone.supportCsfbForSs()) {
+                    throw new RuntimeException("No network to support supplementary services");
+                }
                 if (isActivate() && !mPhone.isClirActivationAndDeactivationPrevented()) {
                     mPhone.mCi.setCLIR(CommandsInterface.CLIR_INVOCATION,
                         obtainMessage(EVENT_SET_COMPLETE, this));
@@ -1001,6 +1077,9 @@
                 }
             } else if (isServiceCodeCallForwarding(mSc)) {
                 Rlog.d(LOG_TAG, "processCode: is CF");
+                if (!mPhone.supportCsfbForSs()) {
+                    throw new RuntimeException("No network to support supplementary services");
+                }
 
                 String dialingNumber = mSia;
                 int serviceClass = siToServiceClass(mSib);
@@ -1048,6 +1127,9 @@
                                     isEnableDesired, this));
                 }
             } else if (isServiceCodeCallBarring(mSc)) {
+                if (!mPhone.supportCsfbForSs()) {
+                    throw new RuntimeException("No network to support supplementary services");
+                }
                 // sia = password
                 // sib = basic service group
 
@@ -1095,15 +1177,32 @@
                 }
 
             } else if (mSc != null && mSc.equals(SC_WAIT)) {
+                if (!mPhone.supportCsfbForSs()) {
+                    throw new RuntimeException("No network to support supplementary services");
+                }
                 // sia = basic service group
                 int serviceClass = siToServiceClass(mSia);
 
                 if (isActivate() || isDeactivate()) {
+                    if (serviceClass == SERVICE_CLASS_NONE
+                            || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+                        if (mPhone.getTerminalBasedCallWaitingState(true)
+                                != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+                            mPhone.setCallWaiting(isActivate(), serviceClass,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                            return;
+                        }
+                    }
                     mPhone.mCi.setCallWaiting(isActivate(), serviceClass,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                 } else if (isInterrogate()) {
-                    mPhone.mCi.queryCallWaiting(serviceClass,
-                            obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    if (mPhone.getTerminalBasedCallWaitingState(true)
+                            != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+                        mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    } else {
+                        mPhone.mCi.queryCallWaiting(serviceClass,
+                                obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
@@ -1827,6 +1926,120 @@
         return this.mCallbackReceiver;
     }
 
+    /**
+     * Returns list of control strings for a supplementary service request
+     * as defined in TS 22.030 6.5
+     * @param requestType request type associated with the supplementary service
+     * @param serviceType supplementary service type
+     * @return list of control strings associated with the supplementary service.
+     */
+    public static ArrayList<String> getControlStrings(SsData.RequestType requestType,
+            SsData.ServiceType serviceType) {
+        ArrayList<String> controlStrings = new ArrayList<>();
+        if (requestType == null || serviceType == null) {
+            return controlStrings;
+        }
+
+        String actionStr = getActionStringFromReqType(requestType);
+        switch (serviceType) {
+            case SS_CFU:
+                controlStrings.add(actionStr + SC_CFU);
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CF_BUSY:
+                controlStrings.add(actionStr + SC_CFB);
+                controlStrings.add(actionStr + SC_CF_All_Conditional);
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CF_NO_REPLY:
+                controlStrings.add(actionStr + SC_CFNRy);
+                controlStrings.add(actionStr + SC_CF_All_Conditional);
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CF_NOT_REACHABLE:
+                controlStrings.add(actionStr + SC_CFNR);
+                controlStrings.add(actionStr + SC_CF_All_Conditional);
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CF_ALL:
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CF_ALL_CONDITIONAL:
+                controlStrings.add(actionStr + SC_CF_All_Conditional);
+                controlStrings.add(actionStr + SC_CF_All);
+                break;
+            case SS_CLIP:
+                controlStrings.add(actionStr + SC_CLIP);
+                break;
+            case SS_CLIR:
+                controlStrings.add(actionStr + SC_CLIR);
+                break;
+            case SS_WAIT:
+                controlStrings.add(actionStr + SC_WAIT);
+                break;
+            case SS_BAOC:
+                controlStrings.add(actionStr + SC_BAOC);
+                controlStrings.add(actionStr + SC_BA_MO);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_BAOIC:
+                controlStrings.add(actionStr + SC_BAOIC);
+                controlStrings.add(actionStr + SC_BA_MO);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_BAOIC_EXC_HOME:
+                controlStrings.add(actionStr + SC_BAOICxH);
+                controlStrings.add(actionStr + SC_BA_MO);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_BAIC:
+                controlStrings.add(actionStr + SC_BAIC);
+                controlStrings.add(actionStr + SC_BA_MT);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_BAIC_ROAMING:
+                controlStrings.add(actionStr + SC_BAICr);
+                controlStrings.add(actionStr + SC_BA_MT);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_ALL_BARRING:
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_OUTGOING_BARRING:
+                controlStrings.add(actionStr + SC_BA_MO);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+            case SS_INCOMING_BARRING:
+                controlStrings.add(actionStr + SC_BA_MT);
+                controlStrings.add(actionStr + SC_BA_ALL);
+                break;
+        }
+       return controlStrings;
+    }
+
+    /**
+     * Returns control strings for registration of new password as per TS 22.030 6.5.4
+     * @param requestType request type associated with the supplementary service
+     * @param serviceType supplementary service type
+     * @return list of control strings for new password registration.
+     */
+    public static ArrayList<String> getControlStringsForPwd(SsData.RequestType requestType,
+            SsData.ServiceType serviceType) {
+        ArrayList<String> controlStrings = new ArrayList<>();
+        if (requestType == null || serviceType == null) {
+            return controlStrings;
+        }
+
+        controlStrings = getControlStrings(SsData.RequestType.SS_ACTIVATION, serviceType);
+        String actionStr = getActionStringFromReqType(requestType);
+        ArrayList<String> controlStringsPwd = new ArrayList<>();
+        for(String controlString : controlStrings) {
+            // Prepend each control string with **SC_PWD
+            controlStringsPwd.add(actionStr + SC_PWD + controlString);
+        }
+        return controlStringsPwd;
+    }
+
     /***
      * TODO: It would be nice to have a method here that can take in a dialstring and
      * figure out if there is an MMI code embedded within it.  This code would replace
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 1f237b2..5b1f36d 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -120,6 +120,21 @@
     }
 
     @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+            int validityPeriod, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+                validityPeriod, messageRef);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message,
+                statusReportRequested, messageRef);
+    }
+
+    @Override
     protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
         return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly);
     }
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index e594ab6..48be16c 100755
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -32,6 +32,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
+import java.util.Locale;
 
 /**
  * This class implements reading and parsing USIM records.
@@ -233,7 +234,7 @@
 
             int emailEfid = email.getEfid();
             log("EF_EMAIL exists in PBR. efid = 0x" +
-                    Integer.toHexString(emailEfid).toUpperCase());
+                    Integer.toHexString(emailEfid).toUpperCase(Locale.ROOT));
 
             /**
              * Make sure this EF_EMAIL was never read earlier. Sometimes two PBR record points
@@ -348,7 +349,7 @@
                 emailList = new ArrayList<String>();
             }
             log("Adding email #" + i + " list to index 0x" +
-                    Integer.toHexString(index).toUpperCase());
+                    Integer.toHexString(index).toUpperCase(Locale.ROOT));
             emailList.add(email);
             mEmailsForAdnRec.put(index, emailList);
         }
@@ -402,7 +403,7 @@
                 }
                 emailList.add(email);
                 log("Adding email list to index 0x" +
-                        Integer.toHexString(index).toUpperCase());
+                        Integer.toHexString(index).toUpperCase(Locale.ROOT));
                 mEmailsForAdnRec.put(index, emailList);
             }
         }
@@ -446,8 +447,9 @@
             System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size());
             rec.setEmails(emails);
             log("Adding email list to ADN (0x" +
-                    Integer.toHexString(mPhoneBookRecords.get(i).getEfid()).toUpperCase() +
-                    ") record #" + mPhoneBookRecords.get(i).getRecId());
+                    Integer.toHexString(mPhoneBookRecords.get(i).getEfid())
+                            .toUpperCase(Locale.ROOT) + ") record #"
+                    + mPhoneBookRecords.get(i).getRecId());
             mPhoneBookRecords.set(i, rec);
         }
     }
diff --git a/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java
new file mode 100644
index 0000000..f7412a4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsEnablementTracker.java
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.ims;
+
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+/**
+ * This class will abstract away all the new enablement logic and take the reset/enable/disable
+ * IMS commands as inputs.
+ * The IMS commands will call enableIms or disableIms to match the enablement state only when
+ * it changes.
+ */
+public class ImsEnablementTracker {
+    private static final String LOG_TAG = "ImsEnablementTracker";
+    private static final long REQUEST_THROTTLE_TIME_MS = 1 * 1000; // 1 seconds
+
+    private static final int COMMAND_NONE_MSG = 0;
+    // Indicate that the enableIms command has been received.
+    private static final int COMMAND_ENABLE_MSG = 1;
+    // Indicate that the disableIms command has been received.
+    private static final int COMMAND_DISABLE_MSG = 2;
+    // Indicate that the resetIms command has been received.
+    private static final int COMMAND_RESET_MSG = 3;
+    // Indicate that the internal enable message with delay has been received.
+    @VisibleForTesting
+    protected static final int COMMAND_ENABLING_DONE = 4;
+    // Indicate that the internal disable message with delay has been received.
+    @VisibleForTesting
+    protected static final int COMMAND_DISABLING_DONE = 5;
+    // Indicate that the internal reset message with delay has been received.
+    @VisibleForTesting
+    protected static final int COMMAND_RESETTING_DONE = 6;
+    // The ImsServiceController binder is connected.
+    private static final int COMMAND_CONNECTED_MSG = 7;
+    // The ImsServiceController binder is disconnected.
+    private static final int COMMAND_DISCONNECTED_MSG = 8;
+
+    @VisibleForTesting
+    protected static final int STATE_IMS_DISCONNECTED = 0;
+    @VisibleForTesting
+    protected static final int STATE_IMS_DEFAULT = 1;
+    @VisibleForTesting
+    protected static final int STATE_IMS_ENABLED = 2;
+    @VisibleForTesting
+    protected static final int STATE_IMS_DISABLING = 3;
+    @VisibleForTesting
+    protected static final int STATE_IMS_DISABLED = 4;
+    @VisibleForTesting
+    protected static final int STATE_IMS_ENABLING = 5;
+    @VisibleForTesting
+    protected static final int STATE_IMS_RESETTING = 6;
+
+    protected final Object mLock = new Object();
+    private IImsServiceController mIImsServiceController;
+    private long mLastImsOperationTimeMs = 0L;
+
+    private final ImsEnablementTrackerStateMachine mEnablementStateMachine;
+
+    /**
+     * Provides Ims Enablement Tracker State Machine responsible for ims enable/disable command
+     * interactions with Ims service controller binder.
+     * The enable/disable/reset ims commands have a time interval of at least 1 second between
+     * processing each command.
+     * For example, the enableIms command is received and the binder's enableIms is called.
+     * After that, if the disableIms command is received, the binder's disableIms will be
+     * called after 1 second.
+     * A time of 1 second uses {@link Handler#sendMessageDelayed(Message, long)},
+     * and the enabled, disabled and reset states are responsible for waiting for
+     * that delay message.
+     */
+    class ImsEnablementTrackerStateMachine extends StateMachine {
+        /**
+         * The initial state of this class and waiting for an ims commands.
+         */
+        @VisibleForTesting
+        public final Default mDefault;
+        /**
+         * Indicates that {@link IImsServiceController#enableIms(int, int)} has been called and
+         * waiting for an ims commands.
+         * Common transitions are to
+         * {@link #mDisabling} state when the disable command is received
+         * or {@link #mResetting} state when the reset command is received.
+         * or {@link #mDisconnected} if the binder is disconnected.
+         */
+        @VisibleForTesting
+        public final Enabled mEnabled;
+        /**
+         * Indicates that the state waiting for a disableIms message.
+         * Common transitions are to
+         * {@link #mEnabled} when the enable command is received.
+         * or {@link #mResetting} when the reset command is received.
+         * or {@link #mDisabled} the previous binder API call has passed 1 second, and if
+         * {@link IImsServiceController#disableIms(int, int)} called.
+         * or {@link #mDisabling} received a disableIms message and the previous binder API call
+         * has not passed 1 second.Then send a disableIms message with delay.
+         * or {@link #mDisconnected} if the binder is disconnected.
+         */
+        @VisibleForTesting
+        public final Disabling mDisabling;
+        /**
+         * Indicates that {@link IImsServiceController#disableIms(int, int)} has been called and
+         * waiting for an ims commands.
+         * Common transitions are to
+         * {@link #mEnabling} state when the enable command is received.
+         * or {@link #mDisconnected} if the binder is disconnected.
+         */
+        @VisibleForTesting
+        public final Disabled mDisabled;
+        /**
+         * Indicates that the state waiting for an enableIms message.
+         * Common transitions are to
+         * {@link #mEnabled} the previous binder API call has passed 1 second, and
+         * {@link IImsServiceController#enableIms(int, int)} called.
+         * or {@link #mDisabled} when the disable command is received.
+         * or {@link #mEnabling} received an enableIms message and the previous binder API call
+         * has not passed 1 second.Then send an enableIms message with delay.
+         * or {@link #mDisconnected} if the binder is disconnected.
+         */
+        @VisibleForTesting
+        public final Enabling mEnabling;
+        /**
+         * Indicates that the state waiting for a resetIms message.
+         * Common transitions are to
+         * {@link #mDisabling} state when the disable command is received
+         * or {@link #mResetting} received a resetIms message and the previous binder API call
+         * has not passed 1 second.Then send a resetIms message with delay.
+         * or {@link #mEnabling} when the resetIms message is received and if
+         * {@link IImsServiceController#disableIms(int, int)} call is successful. And send an enable
+         * message with delay.
+         * or {@link #mDisconnected} if the binder is disconnected.
+         */
+        @VisibleForTesting
+        public final Resetting mResetting;
+        /**
+         * Indicates that {@link IImsServiceController} has not been set.
+         * Common transition is to
+         * {@link #mDefault} state when the binder is set.
+         * or {@link #mDisabling} If the disable command is received while the binder is
+         * disconnected
+         * or {@link #mEnabling} If the enable command is received while the binder is
+         * disconnected
+         */
+        @VisibleForTesting
+        public final Disconnected mDisconnected;
+
+        @VisibleForTesting
+        public int mSlotId;
+        @VisibleForTesting
+        public int mSubId;
+
+        ImsEnablementTrackerStateMachine(String name, Looper looper, int state) {
+            super(name, looper);
+            mDefault = new Default();
+            mEnabled = new Enabled();
+            mDisabling = new Disabling();
+            mDisabled = new Disabled();
+            mEnabling = new Enabling();
+            mResetting = new Resetting();
+            mDisconnected = new Disconnected();
+
+            addState(mDefault);
+            addState(mEnabled);
+            addState(mDisabling);
+            addState(mDisabled);
+            addState(mEnabling);
+            addState(mResetting);
+            addState(mDisconnected);
+
+            setInitialState(getState(state));
+        }
+
+        public void clearAllMessage() {
+            Log.d(LOG_TAG, "clearAllMessage");
+            removeMessages(COMMAND_ENABLE_MSG);
+            removeMessages(COMMAND_DISABLE_MSG);
+            removeMessages(COMMAND_RESET_MSG);
+            removeMessages(COMMAND_ENABLING_DONE);
+            removeMessages(COMMAND_DISABLING_DONE);
+            removeMessages(COMMAND_RESETTING_DONE);
+        }
+
+        public void serviceBinderConnected() {
+            clearAllMessage();
+            sendMessage(COMMAND_CONNECTED_MSG);
+        }
+
+        public void serviceBinderDisconnected() {
+            clearAllMessage();
+            sendMessage(COMMAND_DISCONNECTED_MSG);
+        }
+
+        @VisibleForTesting
+        public boolean isState(int state) {
+            if (state == mDefault.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mDefault) ? true : false;
+            } else if (state == mEnabled.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mEnabled) ? true : false;
+            } else if (state == mDisabling.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mDisabling) ? true : false;
+            } else if (state == mDisabled.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mDisabled) ? true : false;
+            } else if (state == mEnabling.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mEnabling) ? true : false;
+            } else if (state == mResetting.mStateNo) {
+                return (mEnablementStateMachine.getCurrentState() == mResetting) ? true : false;
+            }
+            return false;
+        }
+
+        private State getState(int state) {
+            switch (state) {
+                case ImsEnablementTracker.STATE_IMS_DEFAULT:
+                    return mDefault;
+                case ImsEnablementTracker.STATE_IMS_ENABLED:
+                    return mEnabled;
+                case ImsEnablementTracker.STATE_IMS_DISABLING:
+                    return mDisabling;
+                case ImsEnablementTracker.STATE_IMS_DISABLED:
+                    return mDisabled;
+                case ImsEnablementTracker.STATE_IMS_ENABLING:
+                    return mEnabling;
+                case ImsEnablementTracker.STATE_IMS_RESETTING:
+                    return mResetting;
+                default:
+                    return mDisconnected;
+            }
+        }
+
+        class Default extends State {
+            public int mStateNo = STATE_IMS_DEFAULT;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Default state:enter");
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Default state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Default state:processMessage. msg.what=" + message.what);
+                switch(message.what) {
+                    // When enableIms() is called, enableIms of binder is call and the state
+                    // change to the enabled state.
+                    case COMMAND_ENABLE_MSG:
+                        sendEnableIms(message.arg1, message.arg2);
+                        transitionTo(mEnabled);
+                        return HANDLED;
+                    // When disableIms() is called, disableIms of binder is call and the state
+                    // change to the disabled state.
+                    case COMMAND_DISABLE_MSG:
+                        sendDisableIms(message.arg1, message.arg2);
+                        transitionTo(mDisabled);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Enabled extends State {
+            public int mStateNo = STATE_IMS_ENABLED;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Enabled state:enter");
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Enabled state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Enabled state:processMessage. msg.what=" + message.what);
+                mSlotId = message.arg1;
+                mSubId = message.arg2;
+                switch(message.what) {
+                    // the disableIms() is called.
+                    case COMMAND_DISABLE_MSG:
+                        transitionTo(mDisabling);
+                        return HANDLED;
+                    // the resetIms() is called.
+                    case COMMAND_RESET_MSG:
+                        transitionTo(mResetting);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Disabling extends State {
+            public int mStateNo = STATE_IMS_DISABLING;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Disabling state:enter");
+                sendMessageDelayed(COMMAND_DISABLING_DONE, mSlotId, mSubId,
+                        getRemainThrottleTime());
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Disabling state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Disabling state:processMessage. msg.what=" + message.what);
+                mSlotId = message.arg1;
+                mSubId = message.arg2;
+                switch(message.what) {
+                    // In the enabled state, disableIms() is called, but the throttle timer has
+                    // not expired, so a delay_disable message is sent.
+                    // At this point enableIms() was called, so it cancels the message and just
+                    // changes the state to the enabled.
+                    case COMMAND_ENABLE_MSG:
+                        clearAllMessage();
+                        transitionTo(mEnabled);
+                        return HANDLED;
+                    case COMMAND_DISABLING_DONE:
+                        // If the disable command is received before disableIms is processed,
+                        // it will be ignored because the disable command processing is in progress.
+                        removeMessages(COMMAND_DISABLE_MSG);
+                        sendDisableIms(mSlotId, mSubId);
+                        transitionTo(mDisabled);
+                        return HANDLED;
+                    case COMMAND_RESET_MSG:
+                        clearAllMessage();
+                        transitionTo(mResetting);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Disabled extends State {
+            public int mStateNo = STATE_IMS_DISABLED;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Disabled state:enter");
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Disabled state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Disabled state:processMessage. msg.what=" + message.what);
+                mSlotId = message.arg1;
+                mSubId = message.arg2;
+                switch(message.what) {
+                    case COMMAND_ENABLE_MSG:
+                        transitionTo(mEnabling);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Enabling extends State {
+            public int mStateNo = STATE_IMS_ENABLING;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Enabling state:enter");
+                sendMessageDelayed(COMMAND_ENABLING_DONE, mSlotId, mSubId, getRemainThrottleTime());
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Enabling state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Enabling state:processMessage. msg.what=" + message.what);
+                mSlotId = message.arg1;
+                mSubId = message.arg2;
+                switch(message.what) {
+                    case COMMAND_DISABLE_MSG:
+                        clearAllMessage();
+                        transitionTo(mDisabled);
+                        return HANDLED;
+                    case COMMAND_ENABLING_DONE:
+                        // If the enable command is received before enableIms is processed,
+                        // it will be ignored because the enable command processing is in progress.
+                        removeMessages(COMMAND_ENABLE_MSG);
+                        sendEnableIms(mSlotId, mSubId);
+                        transitionTo(mEnabled);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Resetting extends State {
+            public int mStateNo = STATE_IMS_RESETTING;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Resetting state:enter");
+                sendMessageDelayed(COMMAND_RESETTING_DONE, mSlotId, mSubId,
+                        getRemainThrottleTime());
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Resetting state:exit");
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Resetting state:processMessage. msg.what=" + message.what);
+                mSlotId = message.arg1;
+                mSubId = message.arg2;
+                switch(message.what) {
+                    case COMMAND_DISABLE_MSG:
+                        clearAllMessage();
+                        transitionTo(mDisabling);
+                        return HANDLED;
+                    case COMMAND_RESETTING_DONE:
+                        // If the reset command is received before disableIms is processed,
+                        // it will be ignored because the reset command processing is in progress.
+                        removeMessages(COMMAND_RESET_MSG);
+                        sendDisableIms(mSlotId, mSubId);
+                        transitionTo(mEnabling);
+                        return HANDLED;
+                    case COMMAND_DISCONNECTED_MSG:
+                        transitionTo(mDisconnected);
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+
+        class Disconnected extends State {
+            public int mStateNo = STATE_IMS_DISCONNECTED;
+            private int mLastMsg = COMMAND_NONE_MSG;
+            @Override
+            public void enter() {
+                Log.d(LOG_TAG, "Disconnected state:enter");
+                clearAllMessage();
+            }
+
+            @Override
+            public void exit() {
+                Log.d(LOG_TAG, "Disconnected state:exit");
+                mLastMsg = COMMAND_NONE_MSG;
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                Log.d(LOG_TAG, "Disconnected state:processMessage. msg.what=" + message.what);
+                switch(message.what) {
+                    case COMMAND_CONNECTED_MSG:
+                        clearAllMessage();
+                        transitionTo(mDefault);
+                        if (mLastMsg != COMMAND_NONE_MSG) {
+                            sendMessageDelayed(mLastMsg, mSlotId, mSubId, 0);
+                        }
+                        return HANDLED;
+                    case COMMAND_ENABLE_MSG:
+                    case COMMAND_DISABLE_MSG:
+                    case COMMAND_RESET_MSG:
+                        mLastMsg = message.what;
+                        mSlotId = message.arg1;
+                        mSubId = message.arg2;
+                        return HANDLED;
+                    default:
+                        return NOT_HANDLED;
+                }
+            }
+        }
+    }
+
+    public ImsEnablementTracker(Looper looper) {
+        mIImsServiceController = null;
+        mEnablementStateMachine = new ImsEnablementTrackerStateMachine("ImsEnablementTracker",
+                looper, ImsEnablementTracker.STATE_IMS_DISCONNECTED);
+        mEnablementStateMachine.start();
+    }
+
+    @VisibleForTesting
+    public ImsEnablementTracker(Looper looper, IImsServiceController controller, int state) {
+        mIImsServiceController = controller;
+        mEnablementStateMachine = new ImsEnablementTrackerStateMachine("ImsEnablementTracker",
+                looper, state);
+    }
+
+    /**
+     * This API is for testing purposes only and is used to start a state machine.
+     */
+    @VisibleForTesting
+    public void startStateMachineAsConnected() {
+        if (mEnablementStateMachine == null) {
+            return;
+        }
+        mEnablementStateMachine.start();
+        mEnablementStateMachine.sendMessage(COMMAND_CONNECTED_MSG);
+    }
+
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mEnablementStateMachine.getHandler();
+    }
+
+    /**
+     * Check that the current state and the input state are the same.
+     * @param state the input state.
+     * @return true if the current state and input state are the same or false.
+     */
+    @VisibleForTesting
+    public boolean isState(int state) {
+        return mEnablementStateMachine.isState(state);
+    }
+
+    /**
+     * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
+     * trigger ImsFeature status updates.
+     * @param slotId slot id
+     * @param subId subscription id
+     */
+    public void enableIms(int slotId, int subId) {
+        Log.d(LOG_TAG, "enableIms");
+        mEnablementStateMachine.sendMessage(COMMAND_ENABLE_MSG, slotId, subId);
+    }
+
+    /**
+     * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
+     * trigger ImsFeature capability status to become false.
+     * @param slotId slot id
+     * @param subId subscription id
+     */
+    public void disableIms(int slotId, int subId) {
+        Log.d(LOG_TAG, "disableIms");
+        mEnablementStateMachine.sendMessage(COMMAND_DISABLE_MSG, slotId, subId);
+    }
+
+    /**
+     * Notify ImsService to disable IMS for the framework if current state is enabled.
+     * And notify ImsService back to enable IMS for the framework.
+     * @param slotId slot id
+     * @param subId subscription id
+     */
+    public void resetIms(int slotId, int subId) {
+        Log.d(LOG_TAG, "resetIms");
+        mEnablementStateMachine.sendMessage(COMMAND_RESET_MSG, slotId, subId);
+    }
+
+    /**
+     * Sets the IImsServiceController instance.
+     */
+    protected void setServiceController(IBinder serviceController) {
+        synchronized (mLock) {
+            mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+
+            if (isServiceControllerAvailable()) {
+                mEnablementStateMachine.serviceBinderConnected();
+            } else {
+                mEnablementStateMachine.serviceBinderDisconnected();
+            }
+        }
+    }
+
+    @VisibleForTesting
+    protected long getLastOperationTimeMillis() {
+        return mLastImsOperationTimeMs;
+    }
+
+    /**
+     * Get remaining throttle time value
+     * @return remaining throttle time value
+     */
+    @VisibleForTesting
+    public long getRemainThrottleTime() {
+        long remainTime = REQUEST_THROTTLE_TIME_MS - (System.currentTimeMillis()
+                - getLastOperationTimeMillis());
+        Log.d(LOG_TAG, "getRemainThrottleTime:" + remainTime);
+        if (remainTime < 0) {
+            return 0;
+        }
+        return remainTime;
+    }
+
+    /**
+     * Send internal Message for testing.
+     */
+    @VisibleForTesting
+    public void sendInternalMessage(int msg, int slotId, int subId) {
+        mEnablementStateMachine.sendMessage(msg, slotId, subId);
+    }
+
+    /**
+     * Check to see if the service controller is available.
+     * @return true if available, false otherwise
+     */
+    private boolean isServiceControllerAvailable() {
+        return mIImsServiceController != null;
+    }
+
+    private void sendEnableIms(int slotId, int subId) {
+        Log.d(LOG_TAG, "sendEnableIms");
+        try {
+            synchronized (mLock) {
+                if (isServiceControllerAvailable()) {
+                    mIImsServiceController.enableIms(slotId, subId);
+                    mLastImsOperationTimeMs = System.currentTimeMillis();
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
+        }
+    }
+
+    private void sendDisableIms(int slotId, int subId) {
+        Log.d(LOG_TAG, "sendDisableIms");
+        try {
+            synchronized (mLock) {
+                if (isServiceControllerAvailable()) {
+                    mIImsServiceController.disableIms(slotId, subId);
+                    mLastImsOperationTimeMs = System.currentTimeMillis();
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index c3331d9..d455d60 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -742,6 +742,15 @@
     }
 
     /**
+     * Notify ImsService to disable IMS for the framework.
+     * And notify ImsService back to enable IMS for the framework.
+     */
+    public void resetIms(int slotId) {
+        getImsServiceControllers(slotId).forEach(
+                (controller) -> controller.resetIms(slotId, getSubId(slotId)));
+    }
+
+    /**
      * Returns the ImsRegistration structure associated with the slotId and feature specified.
      */
     public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 92e7d71..72cf472 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -241,6 +241,7 @@
     private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
     private SparseIntArray mSlotIdToSubIdMap;
     private IImsServiceController mIImsServiceController;
+    private final ImsEnablementTracker mImsEnablementTracker;
     // The Capabilities bitmask of the connected ImsService (see ImsService#ImsServiceCapability).
     private long mServiceCapabilities;
     private ImsServiceConnection mImsServiceConnection;
@@ -332,7 +333,7 @@
         mPermissionManager = (LegacyPermissionManager) mContext.getSystemService(
                 Context.LEGACY_PERMISSION_SERVICE);
         mRepo = repo;
-
+        mImsEnablementTracker = new ImsEnablementTracker(mHandlerThread.getLooper());
         mPackageManager = mContext.getPackageManager();
         if (mPackageManager != null) {
             mChangedPackages = mPackageManager.getChangedPackages(mLastSequenceNumber);
@@ -359,6 +360,7 @@
                 mRestartImsServiceRunnable);
         mPermissionManager = null;
         mRepo = repo;
+        mImsEnablementTracker = new ImsEnablementTracker(handler.getLooper());
     }
 
     /**
@@ -553,15 +555,7 @@
      * trigger ImsFeature status updates.
      */
     public void enableIms(int slotId, int subId) {
-        try {
-            synchronized (mLock) {
-                if (isServiceControllerAvailable()) {
-                    mIImsServiceController.enableIms(slotId, subId);
-                }
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
-        }
+        mImsEnablementTracker.enableIms(slotId, subId);
     }
 
     /**
@@ -569,15 +563,15 @@
      * trigger ImsFeature capability status to become false.
      */
     public void disableIms(int slotId, int subId) {
-        try {
-            synchronized (mLock) {
-                if (isServiceControllerAvailable()) {
-                    mIImsServiceController.disableIms(slotId, subId);
-                }
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
-        }
+        mImsEnablementTracker.disableIms(slotId, subId);
+    }
+
+    /**
+     * Notify ImsService to disable IMS for the framework.
+     * And notify ImsService back to enable IMS for the framework
+     */
+    public void resetIms(int slotId, int subId) {
+        mImsEnablementTracker.resetIms(slotId, subId);
     }
 
     /**
@@ -651,6 +645,7 @@
      */
     protected void setServiceController(IBinder serviceController) {
         mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
+        mImsEnablementTracker.setServiceController(serviceController);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 71e85f3..b206b86 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1583,7 +1583,7 @@
     }
 
     @Override
-    public void notifySrvccState(Call.SrvccState state) {
+    public void notifySrvccState(int state) {
         mCT.notifySrvccState(state);
     }
 
@@ -2617,6 +2617,16 @@
     }
 
     @Override
+    public void setTerminalBasedCallWaitingStatus(int state) {
+        mCT.setTerminalBasedCallWaitingStatus(state);
+    }
+
+    @Override
+    public void triggerEpsFallback(int reason, Message response) {
+        mDefaultPhone.triggerEpsFallback(reason, response);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("ImsPhone extends:");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index d19b8d3..f9032bf 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -41,7 +41,6 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
-import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.uicc.IccFileHandler;
 import com.android.telephony.Rlog;
 
@@ -174,11 +173,6 @@
     }
 
     @Override
-    public PhoneConstants.DataState getDataConnectionState() {
-        return PhoneConstants.DataState.DISCONNECTED;
-    }
-
-    @Override
     public @DataActivityType int getDataActivityState() {
         return TelephonyManager.DATA_ACTIVITY_NONE;
     }
@@ -434,10 +428,6 @@
             Message response) {
     }
 
-    public List<DataConnection> getCurrentDataConnectionList () {
-        return null;
-    }
-
     @Override
     public void updateServiceLocation() {
     }
@@ -540,4 +530,14 @@
             notifyPhoneStateChanged();
         }
     }
+
+    @Override
+    public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        return getDefaultPhone().getTerminalBasedCallWaitingState(forCsOnly);
+    }
+
+    @Override
+    public void setTerminalBasedCallWaitingSupported(boolean supported) {
+        getDefaultPhone().setTerminalBasedCallWaitingSupported(supported);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 82dfc6b..1c9ee88 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -16,12 +16,27 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static android.telephony.CarrierConfigManager.ImsVoice.ALERTING_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.BASIC_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.MIDCALL_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.PREALERTING_SRVCC_SUPPORT;
 import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
 import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_WAITING;
+import static android.telephony.ims.ImsService.CAPABILITY_TERMINAL_BASED_CALL_WAITING;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
 import static com.android.internal.telephony.Phone.CS_FALLBACK;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.NetworkStatsManager;
@@ -65,6 +80,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyLocalConnection;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.DataEnabledChangedReason;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
@@ -76,6 +92,8 @@
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -113,11 +131,10 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SrvccConnection;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.d2d.RtpTransport;
 import com.android.internal.telephony.data.DataSettingsManager;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs;
@@ -132,8 +149,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -149,6 +168,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * {@hide}
@@ -554,6 +574,13 @@
         }
     }
 
+    private class SrvccStartedCallback extends ISrvccStartedCallback.Stub {
+        @Override
+        public void onSrvccCallNotified(List<SrvccCall> profiles) {
+            handleSrvccConnectionInfo(profiles);
+        }
+    }
+
     private volatile NetworkStats mVtDataUsageSnapshot = null;
     private volatile NetworkStats mVtDataUsageUidSnapshot = null;
     private final VtDataUsageProvider mVtDataUsageProvider = new VtDataUsageProvider();
@@ -608,6 +635,8 @@
     private boolean mSupportCepOnPeer = true;
     private boolean mSupportD2DUsingRtp = false;
     private boolean mSupportSdpForRtpHeaderExtensions = false;
+    private final List<Integer> mSrvccTypeSupported = new ArrayList<>();
+    private final SrvccStartedCallback mSrvccStartedCallback = new SrvccStartedCallback();
     // Tracks the state of our background/foreground calls while a call hold/swap operation is
     // in progress. Values listed above.
     private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE;
@@ -965,8 +994,7 @@
         }
     };
 
-    // TODO: make @NonNull after removing DataEnabledSettings
-    private DataSettingsManager.DataSettingsManagerCallback mSettingsCallback;
+    private @NonNull DataSettingsManager.DataSettingsManagerCallback mSettingsCallback;
 
     /**
      * Allows the FeatureConnector used to be swapped for easier testing.
@@ -1060,39 +1088,14 @@
         mPhone.getContext().registerReceiver(mReceiver, intentfilter);
         updateCarrierConfiguration(mPhone.getSubId(), getCarrierConfigBundle(mPhone.getSubId()));
 
-        if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
-            mSettingsCallback = new DataSettingsManager.DataSettingsManagerCallback(this::post) {
-                    @Override
-                    public void onDataEnabledChanged(boolean enabled,
-                            @TelephonyManager.DataEnabledChangedReason int reason,
-                            @NonNull String callingPackage) {
-                        int internalReason;
-                        switch (reason) {
-                            case TelephonyManager.DATA_ENABLED_REASON_USER:
-                                internalReason = DataEnabledSettings.REASON_USER_DATA_ENABLED;
-                                break;
-                            case TelephonyManager.DATA_ENABLED_REASON_POLICY:
-                                internalReason = DataEnabledSettings.REASON_POLICY_DATA_ENABLED;
-                                break;
-                            case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
-                                internalReason = DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER;
-                                break;
-                            case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
-                                internalReason = DataEnabledSettings.REASON_THERMAL_DATA_ENABLED;
-                                break;
-                            case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
-                                internalReason = DataEnabledSettings.REASON_OVERRIDE_RULE_CHANGED;
-                                break;
-                            default:
-                                internalReason = DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED;
-                        }
-                        ImsPhoneCallTracker.this.onDataEnabledChanged(enabled, internalReason);
-                    }};
-            mPhone.getDefaultPhone().getDataSettingsManager().registerCallback(mSettingsCallback);
-        } else {
-            mPhone.getDefaultPhone().getDataEnabledSettings().registerForDataEnabledChanged(
-                    this, EVENT_DATA_ENABLED_CHANGED, null);
-        }
+        mSettingsCallback = new DataSettingsManager.DataSettingsManagerCallback(this::post) {
+                @Override
+                public void onDataEnabledChanged(boolean enabled,
+                        @DataEnabledChangedReason int reason,
+                        @NonNull String callingPackage) {
+                    ImsPhoneCallTracker.this.onDataEnabledChanged(enabled, reason);
+                }};
+        mPhone.getDefaultPhone().getDataSettingsManager().registerCallback(mSettingsCallback);
 
         final TelecomManager telecomManager =
                 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
@@ -1202,6 +1205,8 @@
         // For compatibility with apps that still use deprecated intent
         sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP);
         mCurrentlyConnectedSubId = Optional.of(subId);
+
+        initializeTerminalBasedCallWaiting();
     }
 
     /**
@@ -1335,11 +1340,7 @@
 
         clearDisconnected();
         mPhone.getContext().unregisterReceiver(mReceiver);
-        if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
-            mPhone.getDefaultPhone().getDataSettingsManager().unregisterCallback(mSettingsCallback);
-        } else {
-            mPhone.getDefaultPhone().getDataEnabledSettings().unregisterForDataEnabledChanged(this);
-        }
+        mPhone.getDefaultPhone().getDataSettingsManager().unregisterCallback(mSettingsCallback);
         mImsManagerConnector.disconnect();
 
         final NetworkStatsManager statsManager =
@@ -1772,6 +1773,14 @@
         } else {
             log("No carrier ImsReasonInfo mappings defined.");
         }
+
+        mSrvccTypeSupported.clear();
+        int[] srvccType =
+                carrierConfig.getIntArray(CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY);
+        if (srvccType != null && srvccType.length > 0) {
+            mSrvccTypeSupported.addAll(
+                    Arrays.stream(srvccType).boxed().collect(Collectors.toList()));
+        }
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2830,6 +2839,12 @@
             conn.getCall().detach(conn);
             removeConnection(conn);
 
+            // If the call being disconnected was the pending MO call we should clear it.
+            if (mPendingMO == conn) {
+                mPendingMO.finalize();
+                mPendingMO = null;
+            }
+
             // remove conference participants from the cached list when call is disconnected
             List<ConferenceParticipant> cpList = imsCall.getConferenceParticipants();
             if (cpList != null) {
@@ -2855,7 +2870,7 @@
 
     private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) {
         android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider();
-        ImsCallSession callSession = imsCall.getCallSession(); 
+        ImsCallSession callSession = imsCall.getCallSession();
         if (connVideoProvider != null || callSession == null
             || callSession.getVideoCallProvider() == null) {
             return;
@@ -2878,7 +2893,7 @@
     @VisibleForTesting
     public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) {
         if (message != null) {
-            message = message.toLowerCase();
+            message = message.toLowerCase(Locale.ROOT);
         }
         mImsReasonCodeMap.put(new ImsReasonInfoKeyPair(fromCode, message), toCode);
     }
@@ -2899,7 +2914,7 @@
         if (reason == null) {
             reason = "";
         } else {
-            reason = reason.toLowerCase();
+            reason = reason.toLowerCase(Locale.ROOT);
         }
         log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
                 + reason);
@@ -3886,15 +3901,11 @@
         @Override
         public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
             ImsReasonInfo reasonInfo) {
-            // Check with the DCTracker to see if data is enabled; there may be a case when
+            // Check if data is enabled; there may be a case when
             // ImsPhoneCallTracker isn't being informed of the right data enabled state via its
             // registration, so we'll refresh now.
             boolean isDataEnabled;
-            if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
-                isDataEnabled = mPhone.getDefaultPhone().getDataSettingsManager().isDataEnabled();
-            } else {
-                isDataEnabled = mPhone.getDefaultPhone().getDataEnabledSettings().isDataEnabled();
-            }
+            isDataEnabled = mPhone.getDefaultPhone().getDataSettingsManager().isDataEnabled();
 
             if (DBG) {
                 log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech="
@@ -4268,7 +4279,8 @@
             configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
             configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
             if (mPhone != null && mPhone.getContext() != null) {
-                mPhone.getContext().sendBroadcast(configChangedIntent);
+                mPhone.getContext().sendBroadcast(
+                        configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
             }
         }
     };
@@ -4348,21 +4360,55 @@
      * Notify of a change to SRVCC state
      * @param state the new SRVCC state.
      */
-    public void notifySrvccState(Call.SrvccState state) {
+    public void notifySrvccState(int state) {
         if (DBG) log("notifySrvccState state=" + state);
 
-        mSrvccState = state;
+        switch(state) {
+            case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
+                mSrvccState = Call.SrvccState.STARTED;
+                break;
 
-        if (mSrvccState == Call.SrvccState.COMPLETED) {
-            // If the dialing call had ringback, ensure it stops now, otherwise it'll keep playing
-            // afer the SRVCC completes.
-            mForegroundCall.maybeStopRingback();
+            case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
+                mSrvccState = Call.SrvccState.COMPLETED;
 
-            resetState();
-            transferHandoverConnections(mForegroundCall);
-            transferHandoverConnections(mBackgroundCall);
-            transferHandoverConnections(mRingingCall);
-            updatePhoneState();
+                // If the dialing call had ringback, ensure it stops now,
+                // otherwise it'll keep playing afer the SRVCC completes.
+                mForegroundCall.maybeStopRingback();
+
+                resetState();
+                transferHandoverConnections(mForegroundCall);
+                transferHandoverConnections(mBackgroundCall);
+                transferHandoverConnections(mRingingCall);
+                updatePhoneState();
+                break;
+
+            case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
+                mSrvccState = Call.SrvccState.FAILED;
+                break;
+
+            case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
+                mSrvccState = Call.SrvccState.CANCELED;
+                break;
+
+            default:
+                //ignore invalid state
+                return;
+        }
+
+        if (mImsManager != null) {
+            try {
+                if (mSrvccState == Call.SrvccState.STARTED) {
+                    mImsManager.notifySrvccStarted(mSrvccStartedCallback);
+                } else if (mSrvccState == Call.SrvccState.COMPLETED) {
+                    mImsManager.notifySrvccCompleted();
+                } else if (mSrvccState == Call.SrvccState.FAILED) {
+                    mImsManager.notifySrvccFailed();
+                } else if (mSrvccState == Call.SrvccState.CANCELED) {
+                    mImsManager.notifySrvccCanceled();
+                }
+            } catch (ImsException e) {
+                loge("notifySrvccState : exception " + e);
+            }
         }
     }
 
@@ -4749,6 +4795,7 @@
                         + mSupportSdpForRtpHeaderExtensions);
             }
         }
+        pw.println(" mSrvccTypeSupported=" + mSrvccTypeSupported);
         pw.println(" Event Log:");
         pw.increaseIndent();
         mOperationLocalLog.dump(pw);
@@ -5073,10 +5120,9 @@
     /**
      * Handler of data enabled changed event
      * @param enabled True if data is enabled, otherwise disabled.
-     * @param reason Reason for data enabled/disabled. See {@link DataEnabledChangedReason}.
+     * @param reason Reason for data enabled/disabled.
      */
     private void onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason) {
-        // TODO: TelephonyManager.DataEnabledChangedReason instead once DataEnabledSettings is gone
         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
 
         mIsDataEnabled = enabled;
@@ -5096,9 +5142,9 @@
         }
 
         int reasonCode;
-        if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
+        if (reason == TelephonyManager.DATA_ENABLED_REASON_POLICY) {
             reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
-        } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
+        } else if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) {
             reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
         } else {
             // Unexpected code, default to data disabled.
@@ -5111,10 +5157,10 @@
         // Handle video state changes required as a result of data being enabled/disabled.
         handleDataEnabledChange(enabled, reasonCode);
 
-        // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
+        // We do not want to update the ImsConfig for REASON_UNKNOWN, since it can happen before
         // the carrier config has loaded and will deregister IMS.
         if (!mShouldUpdateImsConfigOnDisconnect
-                && reason != DataEnabledSettings.REASON_REGISTERED
+                && reason != TelephonyManager.DATA_ENABLED_REASON_UNKNOWN
                 && mCarrierConfigLoadedForSubscription) {
             // This will call into updateVideoCallFeatureValue and eventually all clients will be
             // asynchronously notified that the availability of VT over LTE has changed.
@@ -5521,4 +5567,158 @@
         }
         return false;
     }
+
+    private void initializeTerminalBasedCallWaiting() {
+        boolean capable = false;
+        if (mImsManager != null) {
+            try {
+                capable = mImsManager.isCapable(CAPABILITY_TERMINAL_BASED_CALL_WAITING);
+            } catch (ImsException e) {
+                loge("initializeTerminalBasedCallWaiting : exception " + e);
+            }
+        }
+        logi("initializeTerminalBasedCallWaiting capable=" + capable);
+        mPhone.setTerminalBasedCallWaitingSupported(capable);
+
+        if (capable) {
+            setTerminalBasedCallWaitingStatus(mPhone.getTerminalBasedCallWaitingState(false));
+        }
+    }
+
+    /**
+     * Notifies the change of the user setting of the terminal-based call waiting service
+     * to IMS service.
+     */
+    public void setTerminalBasedCallWaitingStatus(int state) {
+        if (state == TERMINAL_BASED_NOT_SUPPORTED) return;
+        if (mImsManager != null) {
+            try {
+                log("setTerminalBasedCallWaitingStatus state=" + state);
+                mImsManager.setTerminalBasedCallWaitingStatus(
+                        state == TERMINAL_BASED_ACTIVATED);
+            } catch (ImsException e) {
+                loge("setTerminalBasedCallWaitingStatus : exception " + e);
+            }
+        }
+    }
+
+    /** Send the list of SrvccConnection instances to the radio */
+    public void handleSrvccConnectionInfo(List<SrvccCall> profileList) {
+        mPhone.getDefaultPhone().mCi.setSrvccCallInfo(
+                convertToSrvccConnectionInfo(profileList), null);
+    }
+
+    /** Converts SrvccCall to SrvccConnection. */
+    @VisibleForTesting
+    public SrvccConnection[] convertToSrvccConnectionInfo(List<SrvccCall> profileList) {
+        if (mSrvccTypeSupported.isEmpty() || profileList == null || profileList.isEmpty()) {
+            return null;
+        }
+
+        List<SrvccConnection> connList = new ArrayList<>();
+        for (SrvccCall profile : profileList) {
+            if (isCallProfileSupported(profile)) {
+                addConnection(connList,
+                        profile, findConnection(profile.getCallId()));
+            }
+        }
+
+        if (connList.isEmpty()) return null;
+        return connList.toArray(new SrvccConnection[0]);
+    }
+
+    private boolean isCallProfileSupported(SrvccCall profile) {
+        if (profile == null) return false;
+
+        switch(profile.getPreciseCallState()) {
+            case PRECISE_CALL_STATE_ACTIVE:
+                return mSrvccTypeSupported.contains(BASIC_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_HOLDING:
+                return mSrvccTypeSupported.contains(MIDCALL_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_DIALING:
+                return mSrvccTypeSupported.contains(PREALERTING_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_ALERTING:
+                return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_INCOMING:
+                return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_WAITING:
+                return mSrvccTypeSupported.contains(ALERTING_SRVCC_SUPPORT);
+            case PRECISE_CALL_STATE_INCOMING_SETUP:
+                return mSrvccTypeSupported.contains(PREALERTING_SRVCC_SUPPORT);
+            default:
+                break;
+        }
+        return false;
+    }
+
+    private synchronized ImsPhoneConnection findConnection(String callId) {
+        for (ImsPhoneConnection c : mConnections) {
+            ImsCall imsCall = c.getImsCall();
+            if (imsCall == null) continue;
+            ImsCallSession session = imsCall.getCallSession();
+            if (session == null) continue;
+
+            if (TextUtils.equals(session.getCallId(), callId)) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Update the list of SrvccConnection with the given SrvccCall and ImsPhoneconnection.
+     *
+     * @param destList the list of SrvccConnection the new connection will be added
+     * @param profile the SrvccCall of the connection to be added
+     * @param c the ImsPhoneConnection of the connection to be added
+     */
+    private void addConnection(
+            List<SrvccConnection> destList, SrvccCall profile, ImsPhoneConnection c) {
+        if (destList == null) return;
+        if (profile == null) return;
+
+        int preciseCallState = profile.getPreciseCallState();
+        if (!isAlive(preciseCallState)) return;
+
+        List<ConferenceParticipant> participants = getConferenceParticipants(c);
+        if (participants != null) {
+            for (ConferenceParticipant cp : participants) {
+                if (cp.getState() == android.telecom.Connection.STATE_DISCONNECTED) {
+                    Rlog.i(LOG_TAG, "addConnection participant is disconnected");
+                    continue;
+                }
+                SrvccConnection srvccConnection = new SrvccConnection(cp, preciseCallState);
+                destList.add(srvccConnection);
+            }
+        } else {
+            SrvccConnection srvccConnection =
+                    new SrvccConnection(profile.getImsCallProfile(), c, preciseCallState);
+            destList.add(srvccConnection);
+        }
+    }
+
+    private List<ConferenceParticipant> getConferenceParticipants(ImsPhoneConnection c) {
+        if (!mSrvccTypeSupported.contains(MIDCALL_SRVCC_SUPPORT)) return null;
+
+        ImsCall imsCall = c.getImsCall();
+        if (imsCall == null) return null;
+
+        List<ConferenceParticipant> participants = imsCall.getConferenceParticipants();
+        if (participants == null || participants.isEmpty()) return null;
+        return participants;
+    }
+
+    private static boolean isAlive(int preciseCallState) {
+        switch (preciseCallState) {
+            case PRECISE_CALL_STATE_ACTIVE: return true;
+            case PRECISE_CALL_STATE_HOLDING: return true;
+            case PRECISE_CALL_STATE_DIALING: return true;
+            case PRECISE_CALL_STATE_ALERTING: return true;
+            case PRECISE_CALL_STATE_INCOMING: return true;
+            case PRECISE_CALL_STATE_WAITING: return true;
+            case PRECISE_CALL_STATE_INCOMING_SETUP: return true;
+            default:
+        }
+        return false;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 359079d..3dadd23 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -32,10 +32,14 @@
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+import static com.android.internal.telephony.SsDomainController.SS_CLIP;
+import static com.android.internal.telephony.SsDomainController.SS_CLIR;
+import static com.android.internal.telephony.SsDomainController.SS_COLP;
+import static com.android.internal.telephony.SsDomainController.SS_COLR;
+import static com.android.internal.telephony.SsDomainController.SS_CW;
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
@@ -58,10 +62,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallWaitingController;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.SsDomainController;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.telephony.Rlog;
@@ -487,17 +493,12 @@
 
     static boolean
     isServiceCodeCallBarring(String sc) {
-        Resources resource = Resources.getSystem();
-        if (sc != null) {
-            String[] barringMMI = resource.getStringArray(
-                com.android.internal.R.array.config_callBarringMMI_for_ims);
-            if (barringMMI != null) {
-                for (String match : barringMMI) {
-                    if (sc.equals(match)) return true;
-                }
-            }
-        }
-        return false;
+        return sc != null
+                && (sc.equals(SC_BAOC)
+                || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH)
+                || sc.equals(SC_BAIC) || sc.equals(SC_BAICr)
+                || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO)
+                || sc.equals(SC_BA_MT));
     }
 
     static boolean isPinPukCommand(String sc) {
@@ -509,9 +510,11 @@
      * Whether the dial string is supplementary service code.
      *
      * @param dialString The dial string.
-     * @return true if the dial string is supplementary service code, and {@code false} otherwise.
+     * @return an instance of SsDomainController.SuppServiceRoutingInfo if the dial string
+     * is supplementary service code, and null otherwise.
      */
-    public static boolean isSuppServiceCodes(String dialString, Phone phone) {
+    public static SsDomainController.SuppServiceRoutingInfo getSuppServiceRoutingInfo(
+            String dialString, Phone phone) {
         if (phone != null && phone.getServiceState().getVoiceRoaming()
                 && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
             /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
@@ -520,38 +523,54 @@
             dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
         }
 
+        if (phone == null) return null;
+        return getSuppServiceRoutingInfo(dialString, phone.getSsDomainController());
+    }
+
+    /**
+     * Whether the dial string is supplementary service code.
+     */
+    @VisibleForTesting
+    public static SsDomainController.SuppServiceRoutingInfo getSuppServiceRoutingInfo(
+            String dialString, SsDomainController controller) {
         Matcher m = sPatternSuppService.matcher(dialString);
         if (m.matches()) {
             String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
             if (isServiceCodeCallForwarding(sc)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForCf(scToCallForwardReason(sc));
             } else if (isServiceCodeCallBarring(sc)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForCb(scToBarringFacility(sc));
             } else if (sc != null && sc.equals(SC_CFUT)) {
-                return true;
+                // for backward compatibility, not specified by CarrierConfig
+                return controller.getSsRoutingOverUt();
             } else if (sc != null && sc.equals(SC_CLIP)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForSs(SS_CLIP);
             } else if (sc != null && sc.equals(SC_CLIR)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForSs(SS_CLIR);
             } else if (sc != null && sc.equals(SC_COLP)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForSs(SS_COLP);
             } else if (sc != null && sc.equals(SC_COLR)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForSs(SS_COLR);
             } else if (sc != null && sc.equals(SC_CNAP)) {
-                return true;
+                // for backward compatibility, not specified by CarrierConfig
+                return controller.getSsRoutingOverUt();
             } else if (sc != null && sc.equals(SC_BS_MT)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForCb(
+                        SsDomainController.CB_FACILITY_BIL);
             } else if (sc != null && sc.equals(SC_BAICa)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForCb(
+                        SsDomainController.CB_FACILITY_ACR);
             } else if (sc != null && sc.equals(SC_PWD)) {
-                return true;
+                // for backward compatibility, not specified by CarrierConfig
+                return controller.getSsRoutingOverUt();
             } else if (sc != null && sc.equals(SC_WAIT)) {
-                return true;
+                return controller.getSuppServiceRoutingInfoForSs(SS_CW);
             } else if (isPinPukCommand(sc)) {
-                return true;
+                // for backward compatibility, not specified by CarrierConfig
+                return controller.getSsRoutingOverUt();
             }
         }
-        return false;
+        return null;
     }
 
     static String
@@ -1096,10 +1115,25 @@
                 int serviceClass = siToServiceClass(mSia);
 
                 if (isActivate() || isDeactivate()) {
+                    if (serviceClass == SERVICE_CLASS_NONE
+                            || (serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+                        if (mPhone.getTerminalBasedCallWaitingState(false)
+                                != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+                            mPhone.getDefaultPhone().setCallWaiting(isActivate(), serviceClass,
+                                    obtainMessage(EVENT_SET_COMPLETE, this));
+                            return;
+                        }
+                    }
                     mPhone.setCallWaiting(isActivate(), serviceClass,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                 } else if (isInterrogate()) {
-                    mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    if (mPhone.getTerminalBasedCallWaitingState(false)
+                            != CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED) {
+                        mPhone.getDefaultPhone()
+                                .getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    } else {
+                        mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
+                    }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index bfa081b..cdade42 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -29,7 +29,9 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.DataFailCause;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.ProtocolType;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataService;
@@ -257,6 +259,16 @@
     private void endDataCallSession() {
         mDataCallSession.oosAtEnd = getIsOos();
         mDataCallSession.ongoing = false;
+        // set if this data call is established for internet on the non-Dds
+        SubscriptionInfo subInfo = SubscriptionController.getInstance()
+                .getSubscriptionInfo(mPhone.getSubId());
+        if (mPhone.getSubId() != SubscriptionController.getInstance().getDefaultDataSubId()
+                && ((mDataCallSession.apnTypeBitmask & ApnSetting.TYPE_DEFAULT)
+                == ApnSetting.TYPE_DEFAULT)
+                && subInfo != null && !subInfo.isOpportunistic()) {
+            mDataCallSession.isNonDds = true;
+        }
+
         // store for the data call list event, after DataCall is disconnected and entered into
         // inactive mode
         PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this);
@@ -292,6 +304,7 @@
                 call.handoverFailureCauses.length);
         copy.handoverFailureRat = Arrays.copyOf(call.handoverFailureRat,
                 call.handoverFailureRat.length);
+        copy.isNonDds = call.isNonDds;
         return copy;
     }
 
@@ -316,6 +329,7 @@
         proto.ongoing = true;
         proto.handoverFailureCauses = new int[0];
         proto.handoverFailureRat = new int[0];
+        proto.isNonDds = false;
         return proto;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 5ade0bb..ee4e842 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -16,17 +16,20 @@
 
 package com.android.internal.telephony.metrics;
 
+import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.NetworkType;
+import android.telephony.CellSignalStrength;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.data.DataStallRecoveryManager;
-import com.android.internal.telephony.dataconnection.DcTracker;
 
 /** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
 public class DataStallRecoveryStats {
@@ -41,42 +44,6 @@
     private static final int RECOVERY_ACTION_RADIO_RESTART_MAPPING = 3;
     private static final int RECOVERY_ACTION_RESET_MODEM_MAPPING = 4;
 
-
-    /** TODO: b/214044479 : Remove this function when new data design(Android T) start. */
-    public static void onDataStallEvent(
-            @DcTracker.RecoveryAction int recoveryAction,
-            Phone phone,
-            boolean isRecovered,
-            int durationMillis) {
-        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
-            phone = phone.getDefaultPhone();
-        }
-
-        int carrierId = phone.getCarrierId();
-        int rat = getRat(phone);
-        int band =
-                (rat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(phone);
-        // the number returned here matches the SignalStrength enum we have
-        int signalStrength = phone.getSignalStrength().getLevel();
-        boolean isOpportunistic = getIsOpportunistic(phone);
-        boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
-
-        // Not use this field in Android S, so we send RECOVERED_REASON_NONE for default value.
-        int recoveryReason = 0;
-        TelephonyStatsLog.write(
-                TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
-                carrierId,
-                rat,
-                signalStrength,
-                recoveryAction,
-                isOpportunistic,
-                isMultiSim,
-                band,
-                isRecovered,
-                durationMillis,
-                recoveryReason);
-    }
-
     /**
      * Called when data stall happened.
      *
@@ -111,6 +78,25 @@
             recoveryAction = RECOVERY_ACTION_RESET_MODEM_MAPPING;
         }
 
+        // collect info of the other device in case of DSDS
+        int otherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        // the number returned here matches the NetworkRegistrationState enum we have
+        int otherNetworkRegState = NetworkRegistrationInfo
+                .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+        for (Phone otherPhone : PhoneFactory.getPhones()) {
+            if (otherPhone.getPhoneId() == phone.getPhoneId()) continue;
+            if (!getIsOpportunistic(otherPhone)) {
+                otherSignalStrength = otherPhone.getSignalStrength().getLevel();
+                NetworkRegistrationInfo regInfo = otherPhone.getServiceState()
+                        .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+                if (regInfo != null) {
+                    otherNetworkRegState = regInfo.getRegistrationState();
+                }
+                break;
+            }
+        }
+
         TelephonyStatsLog.write(
                 TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
                 carrierId,
@@ -122,7 +108,9 @@
                 band,
                 isRecovered,
                 durationMillis,
-                reason);
+                reason,
+                otherSignalStrength,
+                otherNetworkRegState);
     }
 
     /** Returns the RAT used for data (including IWLAN). */
diff --git a/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java b/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java
new file mode 100644
index 0000000..51fe20c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DeviceTelephonyPropertiesStats.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import com.android.internal.telephony.PhoneFactory;
+
+/** Metrics for the telephony related properties on the device. */
+public class DeviceTelephonyPropertiesStats {
+    private static final String TAG = DeviceTelephonyPropertiesStats.class.getSimpleName();
+
+    /**
+     * Record whenever the auto data switch feature is toggled.
+     */
+    public static void recordAutoDataSwitchFeatureToggle() {
+        PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+        storage.recordToggledAutoDataSwitch();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 203e011..8a0cd48 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -16,10 +16,6 @@
 
 package com.android.internal.telephony.metrics;
 
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-
 import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
@@ -33,6 +29,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_STATS;
 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION;
 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SHORT_CODE_SMS;
 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS;
 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT;
@@ -53,6 +50,8 @@
 import android.annotation.Nullable;
 import android.app.StatsManager;
 import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.StatsEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -72,6 +71,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequestsV2;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
@@ -86,6 +86,7 @@
 import com.android.internal.util.ConcurrentUtils;
 import com.android.telephony.Rlog;
 
+import java.time.Duration;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
@@ -105,6 +106,10 @@
     /** Disables various restrictions to ease debugging during development. */
     private static final boolean DBG = false; // STOPSHIP if true
 
+    private static final long MILLIS_PER_HOUR = Duration.ofHours(1).toMillis();
+    private static final long MILLIS_PER_MINUTE = Duration.ofMinutes(1).toMillis();
+    private static final long MILLIS_PER_SECOND = Duration.ofSeconds(1).toMillis();
+
     /**
      * Sets atom pull cool down to 23 hours to help enforcing privacy requirement.
      *
@@ -112,7 +117,7 @@
      * that occur once a day.
      */
     private static final long MIN_COOLDOWN_MILLIS =
-            DBG ? 10L * SECOND_IN_MILLIS : 23L * HOUR_IN_MILLIS;
+            DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
 
     /**
      * Buckets with less than these many calls will be dropped.
@@ -123,7 +128,7 @@
 
     /** Bucket size in milliseconds to round call durations into. */
     private static final long DURATION_BUCKET_MILLIS =
-            DBG ? 2L * SECOND_IN_MILLIS : 5L * MINUTE_IN_MILLIS;
+            DBG ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
 
     private static final StatsManager.PullAtomMetadata POLICY_PULL_DAILY =
             new StatsManager.PullAtomMetadata.Builder()
@@ -175,6 +180,7 @@
             registerAtom(PRESENCE_NOTIFY_EVENT, POLICY_PULL_DAILY);
             registerAtom(GBA_EVENT, POLICY_PULL_DAILY);
             registerAtom(PER_SIM_STATUS, null);
+            registerAtom(OUTGOING_SHORT_CODE_SMS, POLICY_PULL_DAILY);
 
             Rlog.d(TAG, "registered");
         } else {
@@ -250,6 +256,8 @@
                 return pullGbaEvent(data);
             case PER_SIM_STATUS:
                 return pullPerSimStatus(data);
+            case OUTGOING_SHORT_CODE_SMS:
+                return pullOutgoingShortCodeSms(data);
             default:
                 Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                 return StatsManager.PULL_SKIP;
@@ -476,14 +484,22 @@
         }
     }
 
-    private static int pullDeviceTelephonyProperties(List<StatsEvent> data) {
+    private int pullDeviceTelephonyProperties(List<StatsEvent> data) {
         Phone[] phones = getPhonesIfAny();
         if (phones.length == 0) {
             return StatsManager.PULL_SKIP;
         }
-
-        data.add(TelephonyStatsLog.buildStatsEvent(DEVICE_TELEPHONY_PROPERTIES,
-                phones[0].isUsingNewDataStack()));
+        boolean isAutoDataSwitchOn = false;
+        for (Phone phone : phones) {
+            // only applies to non-DDS
+            if (phone.getSubId() != SubscriptionManager.getDefaultDataSubscriptionId()) {
+                isAutoDataSwitchOn = phone.getDataSettingsManager().isMobileDataPolicyEnabled(
+                        TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH);
+                break;
+            }
+        }
+        data.add(TelephonyStatsLog.buildStatsEvent(DEVICE_TELEPHONY_PROPERTIES, true,
+                isAutoDataSwitchOn, mStorage.getAutoDataSwitchToggleCount()));
         return StatsManager.PULL_SUCCESS;
     }
 
@@ -692,6 +708,19 @@
         return result;
     }
 
+    private int pullOutgoingShortCodeSms(List<StatsEvent> data) {
+        OutgoingShortCodeSms[] outgoingShortCodeSmsList = mStorage
+                .getOutgoingShortCodeSms(MIN_COOLDOWN_MILLIS);
+        if (outgoingShortCodeSmsList != null) {
+            // Outgoing short code SMS list is already shuffled when SMS were inserted
+            Arrays.stream(outgoingShortCodeSmsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "OUTGOING_SHORT_CODE_SMS pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
     /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
     private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
         mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
@@ -719,7 +748,7 @@
                 state.simSlotIndex,
                 state.isMultiSim,
                 state.carrierId,
-                (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                roundAndConvertMillisToSeconds(state.totalTimeMillis),
                 state.isEmergencyOnly);
     }
 
@@ -728,7 +757,7 @@
                 VOICE_CALL_RAT_USAGE,
                 usage.carrierId,
                 usage.rat,
-                round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS,
+                roundAndConvertMillisToSeconds(usage.totalDurationMillis),
                 usage.callCount);
     }
 
@@ -811,7 +840,9 @@
                 sms.messageId,
                 sms.retryId,
                 sms.intervalMillis,
-                sms.count);
+                sms.count,
+                sms.sendErrorCode,
+                sms.networkErrorCode);
     }
 
     private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) {
@@ -833,11 +864,13 @@
                 dataCallSession.failureCause,
                 dataCallSession.suggestedRetryMillis,
                 dataCallSession.deactivateReason,
-                round(dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS),
+                roundAndConvertMillisToMinutes(
+                        dataCallSession.durationMinutes * MILLIS_PER_MINUTE),
                 dataCallSession.ongoing,
                 dataCallSession.bandAtEnd,
                 dataCallSession.handoverFailureCauses,
-                dataCallSession.handoverFailureRat);
+                dataCallSession.handoverFailureRat,
+                dataCallSession.isNonDds);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -846,19 +879,15 @@
                 stats.carrierId,
                 stats.simSlotIndex,
                 stats.rat,
-                (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int) (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int)
-                        (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
-                                / SECOND_IN_MILLIS),
-                (int) (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int) (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int) (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int)
-                        (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
-                                / SECOND_IN_MILLIS),
-                (int) (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
-                (int) (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+                roundAndConvertMillisToSeconds(stats.registeredMillis),
+                roundAndConvertMillisToSeconds(stats.voiceCapableMillis),
+                roundAndConvertMillisToSeconds(stats.voiceAvailableMillis),
+                roundAndConvertMillisToSeconds(stats.smsCapableMillis),
+                roundAndConvertMillisToSeconds(stats.smsAvailableMillis),
+                roundAndConvertMillisToSeconds(stats.videoCapableMillis),
+                roundAndConvertMillisToSeconds(stats.videoAvailableMillis),
+                roundAndConvertMillisToSeconds(stats.utCapableMillis),
+                roundAndConvertMillisToSeconds(stats.utAvailableMillis));
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
@@ -889,7 +918,7 @@
                 stats.slotId,
                 stats.featureTagName,
                 stats.registrationTech,
-                (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+                roundAndConvertMillisToSeconds(stats.registeredMillis));
     }
 
     private static StatsEvent buildStatsEvent(RcsClientProvisioningStats stats) {
@@ -910,7 +939,7 @@
                 stats.responseType,
                 stats.isSingleRegistrationEnabled,
                 stats.count,
-                (int) (round(stats.stateTimerMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+                roundAndConvertMillisToSeconds(stats.stateTimerMillis));
     }
 
     private static StatsEvent buildStatsEvent(SipDelegateStats stats) {
@@ -919,7 +948,7 @@
                 stats.dimension,
                 stats.carrierId,
                 stats.slotId,
-                (int) (round(stats.uptimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+                roundAndConvertMillisToSeconds(stats.uptimeMillis),
                 stats.destroyReason);
     }
 
@@ -931,7 +960,7 @@
                 stats.featureTagName,
                 stats.sipTransportDeniedReason,
                 stats.sipTransportDeregisteredReason,
-                (int) (round(stats.associatedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+                roundAndConvertMillisToSeconds(stats.associatedMillis));
     }
 
     private static StatsEvent buildStatsEvent(SipMessageResponse stats) {
@@ -991,7 +1020,7 @@
                 stats.serviceIdName,
                 stats.serviceIdVersion,
                 stats.registrationTech,
-                (int) (round(stats.publishedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+                roundAndConvertMillisToSeconds(stats.publishedMillis));
     }
 
     private static StatsEvent buildStatsEvent(UceEventStats stats) {
@@ -1029,6 +1058,14 @@
                 stats.count);
     }
 
+    private static StatsEvent buildStatsEvent(OutgoingShortCodeSms shortCodeSms) {
+        return TelephonyStatsLog.buildStatsEvent(
+                OUTGOING_SHORT_CODE_SMS,
+                shortCodeSms.category,
+                shortCodeSms.xmlVersion,
+                shortCodeSms.shortCodeSmsCount);
+    }
+
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
     private static Phone[] getPhonesIfAny() {
         try {
@@ -1039,8 +1076,21 @@
         }
     }
 
-    /** Returns the value rounded to the bucket. */
-    private static long round(long value, long bucket) {
-        return bucket == 0 ? value : ((value + bucket / 2) / bucket) * bucket;
+    /**
+     * Rounds the duration and converts it from milliseconds to seconds.
+     */
+    private static int roundAndConvertMillisToSeconds(long valueMillis) {
+        long roundedValueMillis = Math.round((double) valueMillis / DURATION_BUCKET_MILLIS)
+                * DURATION_BUCKET_MILLIS;
+        return (int) (roundedValueMillis / MILLIS_PER_SECOND);
+    }
+
+    /**
+     * Rounds the duration and converts it from milliseconds to minutes.
+     */
+    private static int roundAndConvertMillisToMinutes(long valueMillis) {
+        long roundedValueMillis = Math.round((double) valueMillis / DURATION_BUCKET_MILLIS)
+                * DURATION_BUCKET_MILLIS;
+        return (int) (roundedValueMillis / MILLIS_PER_MINUTE);
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index eef88d2..d1a60e8 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -42,6 +42,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequestsV2;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
@@ -159,6 +160,10 @@
     /** Maximum number of GBA Event to store between pulls. */
     private final int mMaxNumGbaEventStats;
 
+
+     /** Maximum number of outgoing short code sms to store between pulls. */
+    private final int mMaxOutgoingShortCodeSms;
+
     /** Stores persist atoms and persist states of the puller. */
     @VisibleForTesting protected PersistAtoms mAtoms;
 
@@ -207,6 +212,7 @@
             mMaxNumUceEventStats = 5;
             mMaxNumPresenceNotifyEventStats = 10;
             mMaxNumGbaEventStats = 5;
+            mMaxOutgoingShortCodeSms = 5;
         } else {
             mMaxNumVoiceCallSessions = 50;
             mMaxNumSms = 25;
@@ -229,6 +235,7 @@
             mMaxNumUceEventStats = 25;
             mMaxNumPresenceNotifyEventStats = 50;
             mMaxNumGbaEventStats = 10;
+            mMaxOutgoingShortCodeSms = 10;
         }
 
         mAtoms = loadAtomsFromFile();
@@ -431,6 +438,14 @@
         }
     }
 
+    /**
+     * Store the number of times auto data switch feature is toggled.
+     */
+    public synchronized void recordToggledAutoDataSwitch() {
+        mAtoms.autoDataSwitchToggleCount++;
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
     /** Adds a new {@link NetworkRequestsV2} to the storage. */
     public synchronized void addNetworkRequestsV2(NetworkRequestsV2 networkRequests) {
         NetworkRequestsV2 existingMetrics = find(networkRequests);
@@ -655,6 +670,18 @@
         }
     }
 
+    /** Adds an outgoing short code sms to the storage. */
+    public synchronized void addOutgoingShortCodeSms(OutgoingShortCodeSms shortCodeSms) {
+        OutgoingShortCodeSms existingOutgoingShortCodeSms = find(shortCodeSms);
+        if (existingOutgoingShortCodeSms != null) {
+            existingOutgoingShortCodeSms.shortCodeSmsCount += 1;
+        } else {
+            mAtoms.outgoingShortCodeSms = insertAtRandomPlace(mAtoms.outgoingShortCodeSms,
+                    shortCodeSms, mMaxOutgoingShortCodeSms);
+        }
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
     /**
      * Returns and clears the voice call sessions if last pulled longer than {@code
      * minIntervalMillis} ago, otherwise returns {@code null}.
@@ -865,6 +892,16 @@
         }
     }
 
+    /** @return the number of times auto data switch mobile data policy is toggled. */
+    public synchronized int getAutoDataSwitchToggleCount() {
+        int count = mAtoms.autoDataSwitchToggleCount;
+        if (count > 0) {
+            mAtoms.autoDataSwitchToggleCount = 0;
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+        }
+        return count;
+    }
+
     /**
      * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than
      * {@code minIntervalMillis} ago, otherwise returns {@code null}.
@@ -1174,6 +1211,24 @@
         return bitmask;
     }
 
+    /**
+     * Returns and clears the OutgoingShortCodeSms if last pulled longer than {@code
+     * minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized OutgoingShortCodeSms[] getOutgoingShortCodeSms(long minIntervalMillis) {
+        if ((getWallTimeMillis() - mAtoms.outgoingShortCodeSmsPullTimestampMillis)
+                > minIntervalMillis) {
+            mAtoms.outgoingShortCodeSmsPullTimestampMillis = getWallTimeMillis();
+            OutgoingShortCodeSms[] previousOutgoingShortCodeSms = mAtoms.outgoingShortCodeSms;
+            mAtoms.outgoingShortCodeSms = new OutgoingShortCodeSms[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return previousOutgoingShortCodeSms;
+        } else {
+            return null;
+        }
+    }
+
     /** Saves {@link PersistAtoms} to a file in private storage immediately. */
     public synchronized void flushAtoms() {
         saveAtomsToFile(0);
@@ -1309,6 +1364,8 @@
                             atoms.unmeteredNetworks,
                             UnmeteredNetworks.class
                     );
+            atoms.outgoingShortCodeSms = sanitizeAtoms(atoms.outgoingShortCodeSms,
+                    OutgoingShortCodeSms.class, mMaxOutgoingShortCodeSms);
 
             // out of caution, sanitize also the timestamps
             atoms.voiceCallRatUsagePullTimestampMillis =
@@ -1357,6 +1414,8 @@
                     sanitizeTimestamp(atoms.presenceNotifyEventPullTimestampMillis);
             atoms.gbaEventPullTimestampMillis =
                     sanitizeTimestamp(atoms.gbaEventPullTimestampMillis);
+            atoms.outgoingShortCodeSmsPullTimestampMillis =
+                    sanitizeTimestamp(atoms.outgoingShortCodeSmsPullTimestampMillis);
 
             return atoms;
         } catch (NoSuchFileException e) {
@@ -1725,6 +1784,20 @@
     }
 
     /**
+     * Returns OutgoingShortCodeSms atom that has same category, xmlVersion as the given one,
+     * or {@code null} if it does not exist.
+     */
+    private @Nullable OutgoingShortCodeSms find(OutgoingShortCodeSms key) {
+        for (OutgoingShortCodeSms shortCodeSms : mAtoms.outgoingShortCodeSms) {
+            if (shortCodeSms.category == key.category
+                    && shortCodeSms.xmlVersion == key.xmlVersion) {
+                return shortCodeSms;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Inserts a new element in a random position in an array with a maximum size.
      *
      * <p>If the array is full, merge with existing item if possible or replace one item randomly.
@@ -1969,6 +2042,7 @@
         atoms.uceEventStatsPullTimestampMillis = currentTime;
         atoms.presenceNotifyEventPullTimestampMillis = currentTime;
         atoms.gbaEventPullTimestampMillis = currentTime;
+        atoms.outgoingShortCodeSmsPullTimestampMillis = currentTime;
 
         Rlog.d(TAG, "created new PersistAtoms");
         return atoms;
@@ -1979,4 +2053,4 @@
         // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
         return System.currentTimeMillis();
     }
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/metrics/RcsStats.java b/src/java/com/android/internal/telephony/metrics/RcsStats.java
index 4a1ff0d..8751d82 100644
--- a/src/java/com/android/internal/telephony/metrics/RcsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/RcsStats.java
@@ -85,6 +85,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
@@ -129,48 +130,51 @@
     private static final Map<String, Integer> FEATURE_TAGS = new HashMap<>();
 
     static {
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_IM);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_SESSION);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim()
+                        .toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER_VIA_SMS);
         FEATURE_TAGS.put(
-                FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim().toLowerCase(),
+                FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim()
+                        .toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING);
         FEATURE_TAGS.put(
-                FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(),
+                FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_POST_CALL);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_MAP);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_SKETCH);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH_VIA_SMS);
         FEATURE_TAGS.put(
-                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim().toLowerCase(),
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim()
+                        .toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION);
         String FeatureTag = FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG;
-        FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG);
         FEATURE_TAGS.put(
-                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(),
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_VERSION_SUPPORTED);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_ROLE);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_MMTEL);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_VIDEO);
-        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(),
+        FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_PRESENCE);
     }
 
@@ -183,34 +187,42 @@
     private static final Map<String, Integer> SERVICE_IDS = new HashMap<>();
 
     static {
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT);
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE
         );
-        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim().toLowerCase(),
+        SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim()
+                        .toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE);
     }
 
@@ -221,33 +233,33 @@
     private static final Map<String, Integer> MESSAGE_TYPE = new HashMap<>();
 
     static {
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_INVITE);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_ACK);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_OPTIONS);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_BYE);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_CANCEL);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_REGISTER);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_PRACK);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_SUBSCRIBE);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_NOTIFY);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_PUBLISH);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_INFO);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_REFER);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_MESSAGE);
-        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(),
+        MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_UPDATE);
     }
 
@@ -1575,28 +1587,28 @@
     /** Get a enum value from pre-defined feature tag name list */
     @VisibleForTesting
     public int convertTagNameToValue(@NonNull String tagName) {
-        return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(),
+        return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM);
     }
 
     /** Get a enum value from pre-defined service id list */
     @VisibleForTesting
     public int convertServiceIdToValue(@NonNull String serviceId) {
-        return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(),
+        return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(Locale.ROOT),
                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM);
     }
 
     /** Get a enum value from pre-defined message type list */
     @VisibleForTesting
     public int convertMessageTypeToValue(@NonNull String messageType) {
-        return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(),
+        return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(Locale.ROOT),
                 TelephonyProtoEnums.SIP_REQUEST_CUSTOM);
     }
 
     /** Get a enum value from pre-defined reason list */
     @VisibleForTesting
     public int convertPresenceNotifyReason(@NonNull String reason) {
-        return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(),
+        return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(Locale.ROOT),
                 PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM);
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
index 48826fd..5e81282 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -58,6 +58,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
 import com.android.telephony.Rlog;
 
@@ -155,46 +156,62 @@
 
     /** Create a new atom when an outgoing SMS is sent. */
     public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
-            @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp,
+            @SmsManager.Result int sendErrorCode, long messageId, boolean isFromDefaultApp,
             long intervalMillis) {
-        onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, errorCode, NO_ERROR_CODE,
+        onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, sendErrorCode, NO_ERROR_CODE,
                 messageId, isFromDefaultApp, intervalMillis);
     }
 
     /** Create a new atom when an outgoing SMS is sent. */
     public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
-            @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId,
+            @SmsManager.Result int sendErrorCode, int networkErrorCode, long messageId,
             boolean isFromDefaultApp, long intervalMillis) {
         OutgoingSms proto =
                 getOutgoingDefaultProto(is3gpp2, isOverIms, messageId, isFromDefaultApp,
                         intervalMillis);
 
+        // The field errorCode is used for up-to-Android-13 devices. From Android 14, sendErrorCode
+        // and networkErrorCode will be used. The field errorCode will be deprecated when most
+        // devices use Android 14 or higher versions.
         if (isOverIms) {
             // Populate error code and result for IMS case
-            proto.errorCode = errorCode;
+            proto.errorCode = sendErrorCode;
             if (fallbackToCs) {
                 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
-            } else if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+            } else if (sendErrorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
                 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
-            } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+            } else if (sendErrorCode != SmsManager.RESULT_ERROR_NONE) {
                 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
             }
         } else {
             // Populate error code and result for CS case
-            if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+            if (sendErrorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
                 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
-            } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+            } else if (sendErrorCode != SmsManager.RESULT_ERROR_NONE) {
                 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
             }
-            proto.errorCode = radioSpecificErrorCode;
-            if (errorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
-                    && radioSpecificErrorCode == NO_ERROR_CODE) {
+            proto.errorCode = networkErrorCode;
+            if (sendErrorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
+                    && networkErrorCode == NO_ERROR_CODE) {
                 proto.errorCode = is3gpp2 ? NO_NETWORK_ERROR_3GPP2 : NO_NETWORK_ERROR_3GPP;
             }
         }
+
+        proto.sendErrorCode = sendErrorCode;
+        proto.networkErrorCode = networkErrorCode;
+
         mAtomsStorage.addOutgoingSms(proto);
     }
 
+    /** Create a new atom when user attempted to send an outgoing short code sms. */
+    public void onOutgoingShortCodeSms(int category, int xmlVersion) {
+        OutgoingShortCodeSms proto = new OutgoingShortCodeSms();
+        proto.category = category;
+        proto.xmlVersion = xmlVersion;
+        proto.shortCodeSmsCount = 1;
+        mAtomsStorage.addOutgoingShortCodeSms(proto);
+    }
+
     /** Creates a proto for a normal single-part {@code IncomingSms} with default values. */
     private IncomingSms getIncomingDefaultProto(boolean is3gpp2,
             @InboundSmsHandler.SmsSource int smsSource) {
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 1a1bbf1..a191aeb 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -446,7 +446,7 @@
         proto.srvccFailureCount = 0L;
         proto.srvccCancellationCount = 0L;
         proto.rttEnabled = false;
-        proto.isEmergency = conn.isEmergencyCall();
+        proto.isEmergency = conn.isEmergencyCall() || conn.isNetworkIdentifiedEmergencyCall();
         proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false;
         proto.isMultiparty = conn.isMultiparty();
         proto.lastKnownRat = rat;
diff --git a/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java b/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
index 8115127..8b34933 100644
--- a/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/NitzStateMachineImpl.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.time.UnixEpochTime;
 import android.app.timedetector.TelephonyTimeSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.content.Context;
@@ -372,7 +373,7 @@
                 builder.addDebugInfo("Clearing time suggestion"
                         + " reason=" + reason);
             } else {
-                TimestampedValue<Long> newNitzTime = nitzSignal.createTimeSignal();
+                UnixEpochTime newNitzTime = nitzSignal.createTimeSignal();
                 builder.setUnixEpochTime(newNitzTime);
                 builder.addDebugInfo("Sending new time suggestion"
                         + " nitzSignal=" + nitzSignal
diff --git a/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java b/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
index 9c7aac9..74b30f8 100644
--- a/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/TimeServiceHelperImpl.java
@@ -18,13 +18,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.time.UnixEpochTime;
 import android.app.timedetector.TelephonyTimeSuggestion;
 import android.app.timedetector.TimeDetector;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TimeZoneDetector;
 import android.content.Context;
 import android.os.SystemClock;
-import android.os.TimestampedValue;
 import android.util.LocalLog;
 
 import com.android.internal.telephony.Phone;
@@ -69,8 +69,9 @@
         Objects.requireNonNull(timeSuggestion);
 
         if (timeSuggestion.getUnixEpochTime() != null) {
-            TimestampedValue<Long> unixEpochTime = timeSuggestion.getUnixEpochTime();
-            TelephonyMetrics.getInstance().writeNITZEvent(mSlotIndex, unixEpochTime.getValue());
+            UnixEpochTime unixEpochTime = timeSuggestion.getUnixEpochTime();
+            TelephonyMetrics.getInstance().writeNITZEvent(
+                    mSlotIndex, unixEpochTime.getUnixEpochTimeMillis());
         }
         mTimeDetector.suggestTelephonyTime(timeSuggestion);
     }
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
new file mode 100644
index 0000000..3c64495
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -0,0 +1,1327 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Telephony;
+import android.provider.Telephony.SimInfo;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * The subscription database manager is the wrapper of {@link SimInfo}
+ * table. It's a full memory cache of the entire subscription database, and it's intended to run
+ * on a separate thread to perform asynchronous database update. The database's cache allows
+ * multi threads to read simultaneously, if no write is ongoing.
+ *
+ * Note that from Android 14, directly writing into the subscription database through content
+ * resolver with {@link SimInfo#CONTENT_URI} will cause cache/db out of sync. All the read/write
+ * to the database should go through {@link SubscriptionManagerService}.
+ */
+public class SubscriptionDatabaseManager extends Handler {
+    private static final String LOG_TAG = "SDM";
+
+    /** Whether enabling verbose debugging message or not. */
+    private static final boolean VDBG = false;
+
+    /** Invalid database row index. */
+    private static final int INVALID_ROW_INDEX = -1;
+
+    /** The context */
+    @NonNull
+    private final Context mContext;
+
+    /** Telephony manager */
+    private final TelephonyManager mTelephonyManager;
+
+    /** UICC controller */
+    private final UiccController mUiccController;
+
+    /**
+     * The read/write lock to protect the entire database access. Using a Re-entrant read lock as
+     * much more read requests are expected than the write requests. All the access to
+     * {@link #mAllSubscriptionInfoInternalCache} needs to be protected by this lock.
+     */
+    @NonNull
+    private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
+
+    /** Local log for most important debug messages. */
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
+
+    /**
+     * The entire subscription database, including subscriptions from inserted, previously inserted
+     * SIMs. This is the full memory cache of the subscription database. The key is the subscription
+     * id. Note all the access to this map needs to be protected by the re-entrant lock
+     * {@link #mReadWriteLock}.
+     *
+     * @see SimInfo
+     */
+    @GuardedBy("mReadWriteLock")
+    @NonNull
+    private final Map<Integer, SubscriptionInfoInternal> mAllSubscriptionInfoInternalCache =
+            new HashMap<>(16);
+
+    /** The mapping from {@link SimInfo} table to {@link SubscriptionInfoInternal} get methods. */
+    private static final Map<String, Function<SubscriptionInfoInternal, ?>>
+            SUBSCRIPTION_GET_METHOD_MAP = Map.ofEntries(
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+                            SubscriptionInfoInternal::getSubscriptionId),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ICC_ID,
+                            SubscriptionInfoInternal::getIccId),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_SIM_SLOT_INDEX,
+                            SubscriptionInfoInternal::getSimSlotIndex),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_DISPLAY_NAME,
+                            SubscriptionInfoInternal::getDisplayName),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_CARRIER_NAME,
+                            SubscriptionInfoInternal::getCarrierName),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_NAME_SOURCE,
+                            SubscriptionInfoInternal::getDisplayNameSource),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_COLOR,
+                            SubscriptionInfoInternal::getIconTint),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_NUMBER,
+                            SubscriptionInfoInternal::getNumber),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_DATA_ROAMING,
+                            SubscriptionInfoInternal::getDataRoaming),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_MCC_STRING,
+                            SubscriptionInfoInternal::getMccString),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_MNC_STRING,
+                            SubscriptionInfoInternal::getMncString),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_EHPLMNS,
+                            SubscriptionInfoInternal::getEhplmns),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_HPLMNS,
+                            SubscriptionInfoInternal::getHplmnsRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_IS_EMBEDDED,
+                            SubscriptionInfoInternal::isEmbeddedRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_CARD_ID,
+                            SubscriptionInfoInternal::getCardString),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ACCESS_RULES,
+                            SubscriptionInfoInternal::getNativeAccessRulesRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+                            SubscriptionInfoInternal::getCarrierConfigAccessRulesRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_IS_REMOVABLE,
+                            SubscriptionInfoInternal::isRemovableEmbeddedRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+                            SubscriptionInfoInternal::isEnhanced4GModeEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_VT_IMS_ENABLED,
+                            SubscriptionInfoInternal::isVideoTelephonyEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_WFC_IMS_ENABLED,
+                            SubscriptionInfoInternal::isWifiCallingEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_WFC_IMS_MODE,
+                            SubscriptionInfoInternal::getWifiCallingMode),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+                            SubscriptionInfoInternal::getWifiCallingModeForRoaming),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+                            SubscriptionInfoInternal::isWifiCallingEnabledForRoamingRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_IS_OPPORTUNISTIC,
+                            SubscriptionInfoInternal::isOpportunisticRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_GROUP_UUID,
+                            SubscriptionInfoInternal::getGroupUuidRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ISO_COUNTRY_CODE,
+                            SubscriptionInfoInternal::getCountryIso),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_CARRIER_ID,
+                            SubscriptionInfoInternal::getCarrierId),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_PROFILE_CLASS,
+                            SubscriptionInfoInternal::getProfileClass),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_SUBSCRIPTION_TYPE,
+                            SubscriptionInfoInternal::getSubscriptionType),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_GROUP_OWNER,
+                            SubscriptionInfoInternal::getGroupOwner),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+                            SubscriptionInfoInternal::getEnabledMobileDataPoliciesRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_IMSI,
+                            SubscriptionInfoInternal::getImsi),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+                            SubscriptionInfoInternal::areUiccApplicationsEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+                            SubscriptionInfoInternal::isRcsUceEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+                            SubscriptionInfoInternal::isCrossSimCallingEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_RCS_CONFIG,
+                            SubscriptionInfoInternal::getRcsConfig),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+                            SubscriptionInfoInternal::getAllowedNetworkTypesForReasons),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_D2D_STATUS_SHARING,
+                            SubscriptionInfoInternal::getDeviceToDeviceStatusSharingPreference),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+                            SubscriptionInfoInternal::isVoImsOptInEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                            SubscriptionInfoInternal::getDeviceToDeviceStatusSharingContacts),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+                            SubscriptionInfoInternal::isNrAdvancedCallingEnabledRaw),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+                            SubscriptionInfoInternal::getNumberFromCarrier),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
+                            SubscriptionInfoInternal::getNumberFromIms),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_PORT_INDEX,
+                            SubscriptionInfoInternal::getPortIndex),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_USAGE_SETTING,
+                            SubscriptionInfoInternal::getUsageSetting),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_TP_MESSAGE_REF,
+                            SubscriptionInfoInternal::getLastUsedTPMessageReference),
+                    new AbstractMap.SimpleImmutableEntry<>(
+                            SimInfo.COLUMN_USER_HANDLE,
+                            SubscriptionInfoInternal::getUserId)
+                    );
+
+    /**
+     * The constructor.
+     *
+     * @param context The context.
+     * @param looper Looper for the handler.
+     */
+    public SubscriptionDatabaseManager(@NonNull Context context, @NonNull Looper looper) {
+        super(looper);
+        mContext = context;
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        mUiccController = UiccController.getInstance();
+        loadFromDatabase();
+    }
+
+    /**
+     * Helper method to get specific field from {@link SubscriptionInfoInternal} by the database
+     * column name. {@link SubscriptionInfoInternal} represent one single record in the
+     * {@link SimInfo} table. So every column has a corresponding get method in
+     * {@link SubscriptionInfoInternal} (except for unused or deprecated columns).
+     *
+     * @param subInfo The subscription info.
+     * @param columnName The corresponding database column name.
+     *
+     * @return The corresponding value from {@link SubscriptionInfoInternal}.
+     */
+    private Object getSubscriptionInfoFieldByColumnName(@NonNull SubscriptionInfoInternal subInfo,
+            @NonNull String columnName) {
+        if (SUBSCRIPTION_GET_METHOD_MAP.containsKey(columnName)) {
+            return SUBSCRIPTION_GET_METHOD_MAP.get(columnName).apply(subInfo);
+        }
+        return null;
+    }
+
+    /**
+     * Comparing the old/new {@link SubscriptionInfoInternal} and create content values for database
+     * update. If any field in the new subscription info is different from the old one, then each
+     * delta will be added into the {@link ContentValues}.
+     *
+     * @param oldSubInfo The old {@link SubscriptionInfoInternal}.
+     * @param newSubInfo The new {@link SubscriptionInfoInternal}.
+     *
+     * @return The delta content values for database update.
+     */
+    @NonNull
+    private ContentValues createDeltaContentValues(@Nullable SubscriptionInfoInternal oldSubInfo,
+            @NonNull SubscriptionInfoInternal newSubInfo) {
+        ContentValues deltaContentValues = new ContentValues();
+
+        for (String columnName : Telephony.SimInfo.getAllColumns()) {
+            Object newValue = getSubscriptionInfoFieldByColumnName(newSubInfo, columnName);
+            if (newValue != null) {
+                Object oldValue = null;
+                if (oldSubInfo != null) {
+                    oldValue = getSubscriptionInfoFieldByColumnName(oldSubInfo, columnName);
+                }
+                // Some columns need special handling. We need to convert them into a format that
+                // is accepted by the database.
+                if (!Objects.equals(oldValue, newValue)) {
+                    deltaContentValues.putObject(columnName, newValue);
+                }
+            }
+        }
+        return deltaContentValues;
+    }
+
+    /**
+     * Synchronously insert a new record into the database. This operation is synchronous because
+     * we need to convert the inserted row index into the subscription id.
+     *
+     * @param contentValues The fields of the subscription to be inserted into the database.
+     *
+     * @return The row index of the new record. {@link #INVALID_ROW_INDEX} if insertion failed.
+     */
+    private int insertNewRecordIntoDatabaseSync(@NonNull ContentValues contentValues) {
+        Objects.requireNonNull(contentValues);
+        Uri uri = mContext.getContentResolver().insert(SimInfo.CONTENT_URI, contentValues);
+        if (uri != null && uri.getLastPathSegment() != null) {
+            logl("insertNewRecordIntoDatabaseSync: Successfully added subscription. subId="
+                    + uri.getLastPathSegment());
+            return Integer.parseInt(uri.getLastPathSegment());
+        } else {
+            loge("insertNewRecordIntoDatabaseSync: Failed to insert subscription into database. "
+                    + "contentValues=" + contentValues);
+        }
+        return INVALID_ROW_INDEX;
+    }
+
+    /**
+     * Insert a new subscription info. The subscription info must have subscription id
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. Note this is a slow method, so be
+     * cautious to call this method.
+     *
+     * @param subInfo The subscription info to update.
+     *
+     * @return The new subscription id. {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} (-1) if
+     * insertion fails.
+     */
+    public int insertSubscriptionInfo(@NonNull SubscriptionInfoInternal subInfo) {
+        Objects.requireNonNull(subInfo);
+        // A new subscription to be inserted must have invalid subscription id.
+        if (SubscriptionManager.isValidSubscriptionId(subInfo.getSubscriptionId())) {
+            throw new RuntimeException("insertSubscriptionInfo: Not a new subscription to "
+                    + "insert. subInfo=" + subInfo);
+        }
+
+        int subId;
+        // Grab the write lock so no other threads can read or write the cache.
+        mReadWriteLock.writeLock().lock();
+        try {
+            // Synchronously insert into the database. Note this should be the only synchronous
+            // write operation performed by the subscription database manager. The reason is that
+            // we need to get the sub id for cache update.
+            subId = insertNewRecordIntoDatabaseSync(createDeltaContentValues(null, subInfo));
+            if (subId > 0) {
+                mAllSubscriptionInfoInternalCache.put(subId, new SubscriptionInfoInternal
+                        .Builder(subInfo)
+                        .setId(subId).build());
+            } else {
+                loge("insertSubscriptionInfo: Failed to insert a new subscription. subInfo="
+                        + subInfo);
+            }
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+        return subId;
+    }
+
+    /**
+     * Update a subscription in database asynchronously.
+     *
+     * @param subId The subscription id of the subscription to be updated.
+     * @param contentValues The fields to be update.
+     */
+    private void updateDatabaseAsync(int subId, @NonNull ContentValues contentValues) {
+        logv("updateDatabaseAsync: prepare to update sub " + subId);
+        // Perform the update in the handler thread asynchronously.
+        post(() -> {
+            int rowsUpdated = mContext.getContentResolver().update(Uri.withAppendedPath(
+                    SimInfo.CONTENT_URI, String.valueOf(subId)), contentValues, null, null);
+            if (rowsUpdated == 1) {
+                logv("updateDatabaseAsync: Successfully updated subscription in the database. "
+                        + "subId=" + subId + ", contentValues= " + contentValues.getValues());
+            } else {
+                loge("updateDatabaseAsync: Unexpected update result. rowsUpdated=" + rowsUpdated
+                        + ", contentValues=" + contentValues);
+            }
+        });
+    }
+
+    /**
+     * Update a certain field of subscription in the database. Also update the subscription cache
+     * {@link #mAllSubscriptionInfoInternalCache}.
+     *
+     * @param subId The subscription id.
+     * @param columnName The database column name from the database table {@link SimInfo}.
+     * @param newValue The new value to update the subscription info cache
+     * {@link #mAllSubscriptionInfoInternalCache}.
+     * @param builderSetMethod The {@link SubscriptionInfo.Builder} method to set a specific field
+     * when constructing the new {@link SubscriptionInfo}. This should be one of the
+     * SubscriptionInfoInternal.Builder.setXxxx method.
+     * @param <T> The type of newValue for subscription cache update.
+     */
+    private <T> void writeDatabaseAndCacheHelper(int subId, @NonNull String columnName,
+            @Nullable T newValue,
+            BiFunction<SubscriptionInfoInternal.Builder, T, SubscriptionInfoInternal.Builder>
+                    builderSetMethod) {
+        ContentValues contentValues = new ContentValues();
+        SubscriptionInfoInternal subInfoCache;
+
+        // Grab the write lock so no other threads can read or write the cache.
+        mReadWriteLock.writeLock().lock();
+        try {
+            subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
+            if (subInfoCache != null) {
+                // Check if the new value is different from the old value in the cache.
+                if (!Objects.equals(getSubscriptionInfoFieldByColumnName(subInfoCache, columnName),
+                        newValue)) {
+                    // If the value is different, then we need to update the cache. Since all fields
+                    // in SubscriptionInfo is final, so we need to create a new SubscriptionInfo.
+                    SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal
+                            .Builder(subInfoCache);
+
+                    // Apply the new value to the builder. This line is equivalent to
+                    // builder.setXxxxxx(newValue);
+                    builder = builderSetMethod.apply(builder, newValue);
+
+                    // Update the subscription database cache.
+                    mAllSubscriptionInfoInternalCache.put(subId, builder.build());
+
+                    // Prepare the content value for update.
+                    contentValues.putObject(columnName, newValue);
+                    // Writing into the database is slow. So do this asynchronously.
+                    updateDatabaseAsync(subId, contentValues);
+                }
+            } else {
+                loge("Subscription doesn't exist. subId=" + subId);
+            }
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Update the database with the {@link SubscriptionInfo}, and also update the cache.
+     *
+     * @param subInfo The new {@link SubscriptionInfo}.
+     */
+    public void updateSubscription(@NonNull SubscriptionInfoInternal subInfo) {
+        Objects.requireNonNull(subInfo);
+
+        // Grab the write lock so no other threads can read or write the cache.
+        mReadWriteLock.writeLock().lock();
+        try {
+            int subId = subInfo.getSubscriptionId();
+            SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(
+                    subInfo.getSubscriptionId());
+            if (subInfoCache == null) {
+                throw new RuntimeException("updateSubscription: subscription does not exist. subId="
+                        + subId);
+            }
+            mAllSubscriptionInfoInternalCache.put(subId, subInfo);
+            // Writing into the database is slow. So do this asynchronously.
+            updateDatabaseAsync(subId, createDeltaContentValues(subInfoCache, subInfo));
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Set the ICCID of the SIM that is associated with the subscription.
+     *
+     * @param subId Subscription id.
+     * @param iccId The ICCID of the SIM that is associated with this subscription.
+     */
+    public void setIccId(int subId, @NonNull String iccId) {
+        Objects.requireNonNull(iccId);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ICC_ID, iccId,
+                SubscriptionInfoInternal.Builder::setIccId);
+    }
+
+    /**
+     * Set the SIM index of the slot that currently contains the subscription. Set to
+     * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+     *
+     * @param subId Subscription id.
+     * @param simSlotIndex The SIM slot index.
+     */
+    public void setSimSlotIndex(int subId, int simSlotIndex) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SIM_SLOT_INDEX, simSlotIndex,
+                SubscriptionInfoInternal.Builder::setSimSlotIndex);
+    }
+
+    /**
+     * Set the name displayed to the user that identifies this subscription. This name is used
+     * in Settings page and can be renamed by the user.
+     *
+     * @param subId Subscription id.
+     * @param displayName The display name.
+     */
+    public void setDisplayName(int subId, @NonNull String displayName) {
+        Objects.requireNonNull(displayName);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DISPLAY_NAME, displayName,
+                SubscriptionInfoInternal.Builder::setDisplayName);
+    }
+
+    /**
+     * Set the name displayed to the user that identifies subscription provider name. This name
+     * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+     *
+     * @param subId Subscription id.
+     * @param carrierName The carrier name.
+     */
+    public void setCarrierName(int subId, @NonNull String carrierName) {
+        Objects.requireNonNull(carrierName);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_NAME, carrierName,
+                SubscriptionInfoInternal.Builder::setCarrierName);
+    }
+
+    /**
+     * Set the source of the display name.
+     *
+     * @param subId Subscription id.
+     * @param displayNameSource The source of the display name.
+     *
+     * @see SubscriptionInfo#getDisplayName()
+     */
+    public void setDisplayNameSource(int subId, @SimDisplayNameSource int displayNameSource) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NAME_SOURCE, displayNameSource,
+                SubscriptionInfoInternal.Builder::setDisplayNameSource);
+    }
+
+    /**
+     * Set the color to be used for tinting the icon when displaying to the user.
+     *
+     * @param subId Subscription id.
+     * @param iconTint The color to be used for tinting the icon when displaying to the user.
+     */
+    public void setIconTint(int subId, int iconTint) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_COLOR, iconTint,
+                SubscriptionInfoInternal.Builder::setIconTint);
+    }
+
+    /**
+     * Set the number presented to the user identify this subscription.
+     *
+     * @param subId Subscription id.
+     * @param number the number presented to the user identify this subscription.
+     */
+    public void setNumber(int subId, @NonNull String number) {
+        Objects.requireNonNull(number);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NUMBER, number,
+                SubscriptionInfoInternal.Builder::setNumber);
+    }
+
+    /**
+     * Set whether user enables data roaming for this subscription or not.
+     *
+     * @param subId Subscription id.
+     * @param dataRoaming Data roaming mode. Either
+     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+     */
+    public void setDataRoaming(int subId, int dataRoaming) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_DATA_ROAMING, dataRoaming,
+                SubscriptionInfoInternal.Builder::setDataRoaming);
+    }
+
+    /**
+     * Set the mobile country code.
+     *
+     * @param subId Subscription id.
+     * @param mcc The mobile country code.
+     */
+    public void setMcc(int subId, @NonNull String mcc) {
+        Objects.requireNonNull(mcc);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MCC_STRING, mcc,
+                SubscriptionInfoInternal.Builder::setMcc);
+    }
+
+    /**
+     * Set the mobile network code.
+     *
+     * @param subId Subscription id.
+     * @param mnc Mobile network code.
+     */
+    public void setMnc(int subId, @NonNull String mnc) {
+        Objects.requireNonNull(mnc);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_MNC_STRING, mnc,
+                SubscriptionInfoInternal.Builder::setMnc);
+    }
+
+    /**
+     * Set EHPLMNs associated with the subscription.
+     *
+     * @param subId Subscription id.
+     * @param ehplmns EHPLMNs associated with the subscription.
+     */
+    public void setEhplmns(int subId, @NonNull String[] ehplmns) {
+        Objects.requireNonNull(ehplmns);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_EHPLMNS, TextUtils.join(",", ehplmns),
+                SubscriptionInfoInternal.Builder::setEhplmns);
+    }
+
+    /**
+     * Set HPLMNs associated with the subscription.
+     *
+     * @param subId Subscription id.
+     * @param hplmns HPLMNs associated with the subscription.
+     */
+    public void setHplmns(int subId, @NonNull String[] hplmns) {
+        Objects.requireNonNull(hplmns);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_HPLMNS, TextUtils.join(",", hplmns),
+                SubscriptionInfoInternal.Builder::setHplmns);
+    }
+
+    /**
+     * Set whether the subscription is from eSIM or not.
+     *
+     * @param subId Subscription id.
+     * @param isEmbedded {@code true} if the subscription is from eSIM.
+     */
+    public void setEmbedded(int subId, boolean isEmbedded) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_EMBEDDED, isEmbedded ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setEmbedded);
+    }
+
+    /**
+     * Set the card string of the SIM card. This is usually the ICCID or EID.
+     *
+     * @param subId Subscription id.
+     * @param cardString The card string of the SIM card.
+     *
+     * @see SubscriptionInfo#getCardString()
+     */
+    public void setCardString(int subId, @NonNull String cardString) {
+        Objects.requireNonNull(cardString);
+        // Also update the public card id.
+        setCardId(subId, mUiccController.convertToPublicCardId(cardString));
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARD_ID, cardString,
+                SubscriptionInfoInternal.Builder::setCardString);
+    }
+
+    /**
+     * Set the card id. This is the non-PII card id converted from
+     * {@link SubscriptionInfoInternal#getCardString()}. This field only exists in
+     * {@link SubscriptionInfo}, but not the database.
+     *
+     * @param subId Subscription id.
+     * @param cardId The card id.
+     */
+    public void setCardId(int subId, int cardId) {
+        // card id does not have a corresponding SimInfo column. So we only update the cache.
+
+        // Grab the write lock so no other threads can read or write the cache.
+        mReadWriteLock.writeLock().lock();
+        try {
+            SubscriptionInfoInternal subInfoCache = mAllSubscriptionInfoInternalCache.get(subId);
+            mAllSubscriptionInfoInternalCache.put(subId,
+                    new SubscriptionInfoInternal.Builder(subInfoCache)
+                            .setCardId(cardId).build());
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Set the native access rules for this subscription, if it is embedded and defines any.
+     * This does not include access rules for non-embedded subscriptions.
+     *
+     * @param subId Subscription id.
+     * @param nativeAccessRules The native access rules for this subscription.
+     */
+    public void setNativeAccessRules(int subId, @NonNull UiccAccessRule[] nativeAccessRules) {
+        Objects.requireNonNull(nativeAccessRules);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES,
+                UiccAccessRule.encodeRules(nativeAccessRules),
+                SubscriptionInfoInternal.Builder::setNativeAccessRules);
+    }
+
+    /**
+     * Set the carrier certificates for this subscription that are saved in carrier configs.
+     * This does not include access rules from the Uicc, whether embedded or non-embedded.
+     *
+     * @param subId Subscription id.
+     * @param carrierConfigAccessRules The carrier certificates for this subscription.
+     */
+    public void setCarrierConfigAccessRules(int subId,
+            @NonNull UiccAccessRule[] carrierConfigAccessRules) {
+        Objects.requireNonNull(carrierConfigAccessRules);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS,
+                UiccAccessRule.encodeRules(carrierConfigAccessRules),
+                SubscriptionInfoInternal.Builder::setCarrierConfigAccessRules);
+    }
+
+    /**
+     * Set whether an embedded subscription is on a removable card. Such subscriptions are
+     * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+     * accessible unless explicitly deleted. Only meaningful when for embedded subscription.
+     *
+     * @param subId Subscription id.
+     * @param isRemovableEmbedded {@code true} if the subscription is from the removable
+     * embedded SIM.
+     */
+    public void setRemovableEmbedded(int subId, boolean isRemovableEmbedded) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_REMOVABLE, isRemovableEmbedded ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setRemovableEmbedded);
+    }
+
+    /**
+     * Set whether enhanced 4G mode is enabled by the user or not.
+     *
+     * @param subId Subscription id.
+     * @param isEnhanced4GModeEnabled whether enhanced 4G mode is enabled by the user or not.
+     */
+    public void setEnhanced4GModeEnabled(int subId, boolean isEnhanced4GModeEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+                isEnhanced4GModeEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setEnhanced4GModeEnabled);
+    }
+
+    /**
+     * Set whether video telephony is enabled by the user or not.
+     *
+     * @param subId Subscription id.
+     * @param isVideoTelephonyEnabled whether video telephony is enabled by the user or not.
+     */
+    public void setVideoTelephonyEnabled(int subId, boolean isVideoTelephonyEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VT_IMS_ENABLED,
+                isVideoTelephonyEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setVideoTelephonyEnabled);
+    }
+
+    /**
+     * Set whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+     *
+     * @param subId Subscription id.
+     * @param isWifiCallingEnabled whether Wi-Fi calling is enabled by the user or not when the
+     * device is not roaming.
+     */
+    public void setWifiCallingEnabled(int subId, boolean isWifiCallingEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ENABLED,
+                isWifiCallingEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setWifiCallingEnabled);
+    }
+
+    /**
+     * Set Wi-Fi calling mode when the device is not roaming.
+     *
+     * @param subId Subscription id.
+     * @param wifiCallingMode Wi-Fi calling mode when the device is not roaming.
+     */
+    public void setWifiCallingMode(int subId,
+            @ImsMmTelManager.WiFiCallingMode int wifiCallingMode) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_MODE, wifiCallingMode,
+                SubscriptionInfoInternal.Builder::setWifiCallingMode);
+    }
+
+    /**
+     * Set Wi-Fi calling mode when the device is roaming.
+     *
+     * @param subId Subscription id.
+     * @param wifiCallingModeForRoaming Wi-Fi calling mode when the device is roaming.
+     */
+    public void setWifiCallingModeForRoaming(int subId,
+            @ImsMmTelManager.WiFiCallingMode int wifiCallingModeForRoaming) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+                wifiCallingModeForRoaming,
+                SubscriptionInfoInternal.Builder::setWifiCallingModeForRoaming);
+    }
+
+    /**
+     * Set whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+     *
+     * @param subId Subscription id.
+     * @param isWifiCallingEnabledForRoaming whether Wi-Fi calling is enabled by the user or not
+     * when the device is roaming.
+     */
+    public void setWifiCallingEnabledForRoaming(int subId, boolean isWifiCallingEnabledForRoaming) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED,
+                isWifiCallingEnabledForRoaming ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setWifiCallingEnabledForRoaming);
+    }
+
+    /**
+     * Set whether the subscription is opportunistic or not.
+     *
+     * @param subId Subscription id.
+     * @param isOpportunistic {@code true} if the subscription is opportunistic.
+     */
+    public void setOpportunistic(int subId, boolean isOpportunistic) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_OPPORTUNISTIC, isOpportunistic ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setOpportunistic);
+    }
+
+    /**
+     * Set the group UUID of the subscription group.
+     *
+     * @param subId Subscription id.
+     * @param groupUuid The group UUID.
+     *
+     * @see SubscriptionInfo#getGroupUuid()
+     */
+    public void setGroupUuid(int subId, @NonNull String groupUuid) {
+        Objects.requireNonNull(groupUuid);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_UUID, groupUuid,
+                SubscriptionInfoInternal.Builder::setGroupUuid);
+    }
+
+    /**
+     * Set the ISO Country code for the subscription's provider.
+     *
+     * @param subId Subscription id.
+     * @param countryIso The ISO country code for the subscription's provider.
+     */
+    public void setCountryIso(int subId, @NonNull String countryIso) {
+        Objects.requireNonNull(countryIso);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ISO_COUNTRY_CODE, countryIso,
+                SubscriptionInfoInternal.Builder::setCountryIso);
+    }
+
+    /**
+     * Set the subscription carrier id.
+     *
+     * @param subId Subscription id.
+     * @param carrierId The carrier id.
+     *
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    public void setCarrierId(int subId, int carrierId) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CARRIER_ID, carrierId,
+                SubscriptionInfoInternal.Builder::setCarrierId);
+    }
+
+    /**
+     * Set the profile class populated from the profile metadata if present.
+     *
+     * @param subId Subscription id.
+     * @param profileClass the profile class populated from the profile metadata if present.
+     *
+     * @see SubscriptionInfo#getProfileClass()
+     */
+    public void setProfileClass(int subId, @ProfileClass int profileClass) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PROFILE_CLASS, profileClass,
+                SubscriptionInfoInternal.Builder::setProfileClass);
+    }
+
+    /**
+     * Set the subscription type.
+     *
+     * @param subId Subscription id.
+     * @param type Subscription type.
+     */
+    public void setSubscriptionType(int subId, @SubscriptionType int type) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SUBSCRIPTION_TYPE, type,
+                SubscriptionInfoInternal.Builder::setType);
+    }
+
+    /**
+     * Set the owner package of group the subscription belongs to.
+     *
+     * @param subId Subscription id.
+     * @param groupOwner Owner package of group the subscription belongs to.
+     */
+    public void setGroupOwner(int subId, @NonNull String groupOwner) {
+        Objects.requireNonNull(groupOwner);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_GROUP_OWNER, groupOwner,
+                SubscriptionInfoInternal.Builder::setGroupOwner);
+    }
+
+    /**
+     * Set the enabled mobile data policies.
+     *
+     * @param subId Subscription id.
+     * @param enabledMobileDataPolicies The enabled mobile data policies.
+     */
+    public void setEnabledMobileDataPolicies(int subId, @NonNull String enabledMobileDataPolicies) {
+        Objects.requireNonNull(enabledMobileDataPolicies);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES,
+                enabledMobileDataPolicies,
+                SubscriptionInfoInternal.Builder::setEnabledMobileDataPolicies);
+    }
+
+    /**
+     * Set the IMSI (International Mobile Subscriber Identity) of the subscription.
+     *
+     * @param subId Subscription id.
+     * @param imsi The IMSI.
+     */
+    public void setImsi(int subId, @NonNull String imsi) {
+        Objects.requireNonNull(imsi);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMSI, imsi,
+                SubscriptionInfoInternal.Builder::setImsi);
+    }
+
+    /**
+     * Set whether Uicc applications are configured to enable or not.
+     *
+     * @param subId Subscription id.
+     * @param areUiccApplicationsEnabled {@code true} if Uicc applications are configured to
+     * enable.
+     */
+    public void setUiccApplicationsEnabled(int subId, boolean areUiccApplicationsEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED,
+                areUiccApplicationsEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setUiccApplicationsEnabled);
+    }
+
+    /**
+     * Set whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+     * subscription.
+     *
+     * @param subId Subscription id.
+     * @param isRcsUceEnabled If the user enabled RCS UCE for this subscription.
+     */
+    public void setRcsUceEnabled(int subId, boolean isRcsUceEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+                isRcsUceEnabled ? 1 : 0, SubscriptionInfoInternal.Builder::setRcsUceEnabled);
+    }
+
+    /**
+     * Set whether the user has enabled cross SIM calling for this subscription.
+     *
+     * @param subId Subscription id.
+     * @param isCrossSimCallingEnabled If the user enabled cross SIM calling for this
+     * subscription.
+     */
+    public void setCrossSimCallingEnabled(int subId, boolean isCrossSimCallingEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED,
+                isCrossSimCallingEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setCrossSimCallingEnabled);
+    }
+
+    /**
+     * Set the RCS config for this subscription.
+     *
+     * @param subId Subscription id.
+     * @param rcsConfig The RCS config for this subscription.
+     */
+    public void setRcsConfig(int subId, @NonNull byte[] rcsConfig) {
+        Objects.requireNonNull(rcsConfig);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_RCS_CONFIG, rcsConfig,
+                SubscriptionInfoInternal.Builder::setRcsConfig);
+    }
+
+    /**
+     * Set the allowed network types for reasons.
+     *
+     * @param subId Subscription id.
+     * @param allowedNetworkTypesForReasons The allowed network types for reasons in string
+     * format. The format is
+     * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+     *
+     * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+     *
+     */
+    public void setAllowedNetworkTypesForReasons(int subId,
+            @NonNull String allowedNetworkTypesForReasons) {
+        Objects.requireNonNull(allowedNetworkTypesForReasons);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+                allowedNetworkTypesForReasons,
+                SubscriptionInfoInternal.Builder::setAllowedNetworkTypesForReasons);
+    }
+
+    /**
+     * Set device to device sharing status.
+     *
+     * @param subId Subscription id.
+     * @param deviceToDeviceStatusSharingPreference Device to device sharing status.
+     */
+    public void setDeviceToDeviceStatusSharingPreference(int subId,
+            @DeviceToDeviceStatusSharingPreference int deviceToDeviceStatusSharingPreference) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING,
+                deviceToDeviceStatusSharingPreference,
+                SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingPreference);
+    }
+
+    /**
+     * Set whether the user has opted-in voice over IMS.
+     *
+     * @param subId Subscription id.
+     * @param isVoImsOptInEnabled Whether the user has opted-in voice over IMS.
+     */
+    public void setVoImsOptInEnabled(int subId, boolean isVoImsOptInEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
+                isVoImsOptInEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setVoImsOptInEnabled);
+    }
+
+    /**
+     * Set contacts information that allow device to device sharing.
+     *
+     * @param subId Subscription id.
+     * @param deviceToDeviceStatusSharingContacts contacts information that allow device to
+     * device sharing.
+     */
+    public void setDeviceToDeviceStatusSharingContacts(int subId,
+            @NonNull String deviceToDeviceStatusSharingContacts) {
+        Objects.requireNonNull(deviceToDeviceStatusSharingContacts);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                deviceToDeviceStatusSharingContacts,
+                SubscriptionInfoInternal.Builder::setDeviceToDeviceStatusSharingContacts);
+    }
+
+    /**
+     * Set whether the user has enabled NR advanced calling.
+     *
+     * @param subId Subscription id.
+     * @param isNrAdvancedCallingEnabled Whether the user has enabled NR advanced calling.
+     */
+    public void setNrAdvancedCallingEnabled(int subId, boolean isNrAdvancedCallingEnabled) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
+                isNrAdvancedCallingEnabled ? 1 : 0,
+                SubscriptionInfoInternal.Builder::setNrAdvancedCallingEnabled);
+    }
+
+    /**
+     * Set the phone number retrieved from carrier.
+     *
+     * @param subId Subscription id.
+     * @param numberFromCarrier The phone number retrieved from carrier.
+     */
+    public void setNumberFromCarrier(int subId, @NonNull String numberFromCarrier) {
+        Objects.requireNonNull(numberFromCarrier);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER,
+                numberFromCarrier, SubscriptionInfoInternal.Builder::setNumberFromCarrier);
+    }
+
+    /**
+     * Set the phone number retrieved from IMS.
+     *
+     * @param subId Subscription id.
+     * @param numberFromIms The phone number retrieved from IMS.
+     */
+    public void setNumberFromIms(int subId, @NonNull String numberFromIms) {
+        Objects.requireNonNull(numberFromIms);
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
+                numberFromIms, SubscriptionInfoInternal.Builder::setNumberFromIms);
+    }
+
+    /**
+     * Set the port index of the Uicc card.
+     *
+     * @param subId Subscription id.
+     * @param portIndex The port index of the Uicc card.
+     */
+    public void setPortIndex(int subId, int portIndex) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_PORT_INDEX, portIndex,
+                SubscriptionInfoInternal.Builder::setPortIndex);
+    }
+
+    /**
+     * Set subscription's preferred usage setting.
+     *
+     * @param subId Subscription id.
+     * @param usageSetting Subscription's preferred usage setting.
+     */
+    public void setUsageSetting(int subId, @UsageSetting int usageSetting) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USAGE_SETTING, usageSetting,
+                SubscriptionInfoInternal.Builder::setUsageSetting);
+    }
+
+    /**
+     * Set last used TP message reference.
+     *
+     * @param subId Subscription id.
+     * @param lastUsedTPMessageReference Last used TP message reference.
+     */
+    public void setLastUsedTPMessageReference(int subId, int lastUsedTPMessageReference) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_TP_MESSAGE_REF,
+                lastUsedTPMessageReference,
+                SubscriptionInfoInternal.Builder::setLastUsedTPMessageReference);
+    }
+
+    /**
+     * Set the user id associated with this subscription.
+     *
+     * @param subId Subscription id.
+     * @param userId The user id associated with this subscription.
+     */
+    public void setUserId(int subId, int userId) {
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_USER_HANDLE, userId,
+                SubscriptionInfoInternal.Builder::setUserId);
+    }
+
+    /**
+     * Load the entire database into the cache.
+     */
+    private void loadFromDatabase() {
+        // Perform the task in the handler thread.
+        post(() -> {
+            try (Cursor cursor = mContext.getContentResolver().query(
+                    SimInfo.CONTENT_URI, null, null, null, null)) {
+                mReadWriteLock.writeLock().lock();
+                try {
+                    mAllSubscriptionInfoInternalCache.clear();
+                    while (cursor != null && cursor.moveToNext()) {
+                        SubscriptionInfoInternal subInfo = createSubscriptionInfoFromCursor(cursor);
+                        if (subInfo != null) {
+                            mAllSubscriptionInfoInternalCache
+                                    .put(subInfo.getSubscriptionId(), subInfo);
+                        }
+                    }
+                    log("Loaded " + mAllSubscriptionInfoInternalCache.size()
+                            + " records from the subscription database.");
+                } finally {
+                    mReadWriteLock.writeLock().unlock();
+                }
+            }
+        });
+    }
+
+    /**
+     * Build the {@link SubscriptionInfoInternal} from database.
+     *
+     * @param cursor  The cursor of the database.
+     *
+     * @return The subscription info from a single database record.
+     */
+    @Nullable
+    private SubscriptionInfoInternal createSubscriptionInfoFromCursor(@NonNull Cursor cursor) {
+        SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder();
+        int id = cursor.getInt(cursor.getColumnIndexOrThrow(
+                SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        builder.setId(id)
+                .setIccId(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_ICC_ID))))
+                .setSimSlotIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_SIM_SLOT_INDEX)))
+                .setDisplayName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_DISPLAY_NAME))))
+                .setCarrierName(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_CARRIER_NAME))))
+                .setDisplayNameSource(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_NAME_SOURCE)))
+                .setIconTint(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_COLOR)))
+                .setNumber(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_NUMBER))))
+                .setDataRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_DATA_ROAMING)))
+                .setMcc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_MCC_STRING))))
+                .setMnc(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_MNC_STRING))))
+                .setEhplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_EHPLMNS))))
+                .setHplmns(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_HPLMNS))))
+                .setEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_IS_EMBEDDED)));
+
+        String cardString = TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                SimInfo.COLUMN_CARD_ID)));
+        builder.setCardString(cardString);
+        // publicCardId is the publicly exposed int card ID
+        int publicCardId = mUiccController.convertToPublicCardId(cardString);
+
+        builder.setCardId(publicCardId)
+                .setNativeAccessRules(cursor.getBlob(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_ACCESS_RULES)))
+                .setCarrierConfigAccessRules(cursor.getBlob(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS)))
+                .setRemovableEmbedded(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_IS_REMOVABLE)))
+                .setEnhanced4GModeEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED)))
+                .setVideoTelephonyEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_VT_IMS_ENABLED)))
+                .setWifiCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_WFC_IMS_ENABLED)))
+                .setWifiCallingMode(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_WFC_IMS_MODE)))
+                .setWifiCallingModeForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_WFC_IMS_ROAMING_MODE)))
+                .setWifiCallingEnabledForRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED)))
+                .setOpportunistic(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_IS_OPPORTUNISTIC)))
+                .setGroupUuid(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_GROUP_UUID))))
+                .setCountryIso(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_ISO_COUNTRY_CODE))))
+                .setCarrierId(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_CARRIER_ID)))
+                .setProfileClass(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_PROFILE_CLASS)))
+                .setType(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_SUBSCRIPTION_TYPE)))
+                .setGroupOwner(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_GROUP_OWNER))))
+                .setEnabledMobileDataPolicies(TextUtils.emptyIfNull(
+                        cursor.getString(cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES))))
+                .setImsi(TextUtils.emptyIfNull(cursor.getString(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_IMSI))))
+                .setUiccApplicationsEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED)))
+                .setRcsUceEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)))
+                .setCrossSimCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)))
+                .setRcsConfig(cursor.getBlob(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_RCS_CONFIG)))
+                .setAllowedNetworkTypesForReasons(TextUtils.emptyIfNull(
+                        cursor.getString(cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS))))
+                .setDeviceToDeviceStatusSharingPreference(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_D2D_STATUS_SHARING)))
+                .setVoImsOptInEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)))
+                .setDeviceToDeviceStatusSharingContacts(TextUtils.emptyIfNull(cursor.getString(
+                        cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS))))
+                .setNrAdvancedCallingEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED)))
+                .setNumberFromCarrier(TextUtils.emptyIfNull(cursor.getString(
+                        cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER))))
+                .setNumberFromIms(TextUtils.emptyIfNull(cursor.getString(
+                        cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS))))
+                .setPortIndex(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_PORT_INDEX)))
+                .setUsageSetting(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_USAGE_SETTING)))
+                .setLastUsedTPMessageReference(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_TP_MESSAGE_REF)))
+                .setUserId(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        SimInfo.COLUMN_USER_HANDLE)));
+
+        return builder.build();
+    }
+
+    /**
+     * Get the subscription info by subscription id.
+     *
+     * @param subId The subscription id
+     *
+     * @return The subscription info. {@code null} if not found.
+     */
+    @Nullable
+    public SubscriptionInfoInternal getSubscriptionInfoInternal(int subId) {
+        mReadWriteLock.readLock().lock();
+        try {
+            return mAllSubscriptionInfoInternalCache.get(subId);
+        } finally {
+            mReadWriteLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Log debug messages.
+     *
+     * @param s debug messages
+     */
+    private void log(@NonNull String s) {
+        Rlog.d(LOG_TAG, s);
+    }
+
+    /**
+     * Log error messages.
+     *
+     * @param s error messages
+     */
+    private void loge(@NonNull String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+
+    /**
+     * Log verbose messages.
+     *
+     * @param s debug messages.
+     */
+    private void logv(@NonNull String s) {
+        if (VDBG) Rlog.v(LOG_TAG, s);
+    }
+
+    /**
+     * Log debug messages and also log into the local log.
+     *
+     * @param s debug messages
+     */
+    private void logl(@NonNull String s) {
+        log(s);
+        mLocalLog.log(s);
+    }
+
+    /**
+     * Dump the state of {@link SubscriptionManagerService}.
+     *
+     * @param fd File descriptor
+     * @param printWriter Print writer
+     * @param args Arguments
+     */
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter printWriter,
+            @NonNull String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println(SubscriptionManagerService.class.getSimpleName() + ":");
+        pw.increaseIndent();
+        pw.println("All subscriptions:");
+        pw.increaseIndent();
+        mAllSubscriptionInfoInternalCache.forEach((subId, subInfo) -> pw.println(subInfo));
+        pw.decreaseIndent();
+        pw.decreaseIndent();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
new file mode 100644
index 0000000..ae0c1d1
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -0,0 +1,2166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+import android.provider.Telephony.SimInfo;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.DeviceToDeviceStatusSharingPreference;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
+
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The class represents a single row of {@link SimInfo} table. All columns (excepts unused columns)
+ * in the database have a corresponding field in this class.
+ *
+ * The difference between {@link SubscriptionInfo} and this class is that {@link SubscriptionInfo}
+ * is a subset of this class. This is intended to solve the problem that some database fields
+ * required higher permission like
+ * {@link android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE} to access while
+ * {@link SubscriptionManager#getActiveSubscriptionIdList()} only requires
+ * {@link android.Manifest.permission.READ_PHONE_STATE} to access. Sometimes blanking out fields in
+ * {@link SubscriptionInfo} creates ambiguity for clients hard to distinguish between insufficient
+ * permission versus true failure.
+ *
+ * Also the fields in this class match the format used in database. For example, boolean values
+ * are stored as integer, or string arrays are stored as a single comma separated string.
+ */
+public class SubscriptionInfoInternal {
+    /**
+     * Subscription Identifier, this is a device unique number
+     * and not an index into an array
+     */
+    private final int mId;
+
+    /**
+     * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+     */
+    @NonNull
+    private final String mIccId;
+
+    /**
+     * The index of the SIM slot that currently contains the subscription and not necessarily unique
+     * and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the subscription
+     * is inactive.
+     */
+    private final int mSimSlotIndex;
+
+    /**
+     * The name displayed to the user that identifies this subscription. This name is used
+     * in Settings page and can be renamed by the user.
+     */
+    @NonNull
+    private final String mDisplayName;
+
+    /**
+     * The name displayed to the user that identifies subscription provider name. This name is the
+     * SPN displayed in status bar and many other places. Can't be renamed by the user.
+     */
+    @NonNull
+    private final String mCarrierName;
+
+    /**
+     * The source of the {@link #mDisplayName}.
+     */
+    @SimDisplayNameSource
+    private final int mDisplayNameSource;
+
+    /**
+     * The color to be used for tinting the icon when displaying to the user.
+     */
+    private final int mIconTint;
+
+    /**
+     * The number presented to the user identify this subscription.
+     */
+    @NonNull
+    private final String mNumber;
+
+    /**
+     * Whether user enables data roaming for this subscription or not. Either
+     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+     */
+    private final int mDataRoaming;
+
+    /**
+     * Mobile Country Code.
+     */
+    @NonNull
+    private final String mMcc;
+
+    /**
+     * Mobile Network Code.
+     */
+    @NonNull
+    private final String mMnc;
+
+    /**
+     * EHPLMNs associated with the subscription.
+     */
+    @NonNull
+    private final String mEhplmns;
+
+    /**
+     * HPLMNs associated with the subscription.
+     */
+    @NonNull
+    private final String mHplmns;
+
+    /**
+     * Whether the subscription is from eSIM. It is intended to use integer to fit the database
+     * format.
+     */
+    private final int mIsEmbedded;
+
+    /**
+     * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+     * EID for an eUICC card.
+     */
+    @NonNull
+    private final String mCardString;
+
+    /**
+     * The access rules for this subscription, if it is embedded and defines any. This does not
+     * include access rules for non-embedded subscriptions.
+     */
+    @NonNull
+    private final byte[] mNativeAccessRules;
+
+    /**
+     * The carrier certificates for this subscription that are saved in carrier configs.
+     * This does not include access rules from the Uicc, whether embedded or non-embedded.
+     */
+    @NonNull
+    private final byte[] mCarrierConfigAccessRules;
+
+    /**
+     * Whether an embedded subscription is on a removable card. Such subscriptions are marked
+     * inaccessible as soon as the current card is removed. Otherwise, they will remain accessible
+     * unless explicitly deleted. Only meaningful when {@link #isEmbedded()} is {@code true}. It
+     * is intended to use integer to fit the database format.
+     */
+    private final int mIsRemovableEmbedded;
+
+    /**
+     * Whether enhanced 4G mode is enabled by the user or not. It is intended to use integer to fit
+     * the database format.
+     */
+    private final int mIsEnhanced4GModeEnabled;
+
+    /**
+     * Whether video telephony is enabled by the user or not. It is intended to use integer to fit
+     * the database format.
+     */
+    private final int mIsVideoTelephonyEnabled;
+
+    /**
+     * Whether Wi-Fi calling is enabled by the user or not when the device is not roaming. It is
+     * intended to use integer to fit the database format.
+     */
+    private final int mIsWifiCallingEnabled;
+
+    /**
+     * Wi-Fi calling mode when the device is not roaming.
+     */
+    @ImsMmTelManager.WiFiCallingMode
+    private final int mWifiCallingMode;
+
+    /**
+     * Wi-Fi calling mode when the device is roaming.
+     */
+    @ImsMmTelManager.WiFiCallingMode
+    private final int mWifiCallingModeForRoaming;
+
+    /**
+     * Whether Wi-Fi calling is enabled by the user or not when the device is roaming. It is
+     * intended to use integer to fit the database format.
+     */
+    private final int mIsWifiCallingEnabledForRoaming;
+
+    /**
+     * Whether the subscription is opportunistic. It is intended to use integer to fit the database
+     * format.
+     */
+    private final int mIsOpportunistic;
+
+    /**
+     * A UUID assigned to the subscription group in string format.
+     *
+     * @see SubscriptionManager#createSubscriptionGroup(List)
+     */
+    @NonNull
+    private final String mGroupUuid;
+
+    /**
+     * ISO Country code for the subscription's provider.
+     */
+    @NonNull
+    private final String mCountryIso;
+
+    /**
+     * The subscription carrier id.
+     *
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    private final int mCarrierId;
+
+    /**
+     * The profile class populated from the profile metadata if present. Otherwise,
+     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+     * {@code false}).
+     */
+    @ProfileClass
+    private final int mProfileClass;
+
+    /**
+     * Type of the subscription.
+     */
+    @SubscriptionType
+    private final int mType;
+
+    /**
+     * A package name that specifies who created the group. Empty if not available.
+     */
+    @NonNull
+    private final String mGroupOwner;
+
+    /**
+     * The enabled mobile data policies in string format.
+     */
+    @NonNull
+    private final String mEnabledMobileDataPolicies;
+
+    /**
+     * The IMSI (International Mobile Subscriber Identity) of the subscription.
+     */
+    @NonNull
+    private final String mImsi;
+
+    /**
+     * Whether uicc applications are configured to enable or disable.
+     * By default it's true. It is intended to use integer to fit the database format.
+     */
+    private final int mAreUiccApplicationsEnabled;
+
+    /**
+     * Whether the user has enabled IMS RCS User Capability Exchange (UCE) for this subscription.
+     * It is intended to use integer to fit the database format.
+     */
+    private final int mIsRcsUceEnabled;
+
+    /**
+     * Whether the user has enabled cross SIM calling for this subscription. It is intended to
+     * use integer to fit the database format.
+     */
+    private final int mIsCrossSimCallingEnabled;
+
+    /**
+     * The RCS configuration.
+     */
+    @NonNull
+    private final byte[] mRcsConfig;
+
+    /**
+     * The allowed network types for reasons in string format. The format is
+     * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+     *
+     * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+     */
+    @NonNull
+    private final String mAllowedNetworkTypesForReasons;
+
+    /**
+     * Device to device sharing status.
+     */
+    @DeviceToDeviceStatusSharingPreference
+    private final int mDeviceToDeviceStatusSharingPreference;
+
+    /**
+     * Whether the user has opted-in voice over IMS. It is intended to use integer to fit the
+     * database format.
+     */
+    private final int mIsVoImsOptInEnabled;
+
+    /**
+     * Contacts information that allow device to device sharing.
+     */
+    @NonNull
+    private final String mDeviceToDeviceStatusSharingContacts;
+
+    /**
+     * Whether the user has enabled NR advanced calling. It is intended to use integer to fit the
+     * database format.
+     */
+    private final int mIsNrAdvancedCallingEnabled;
+
+    /**
+     * The phone number retrieved from carrier.
+     */
+    @NonNull
+    private final String mNumberFromCarrier;
+
+    /**
+     * The phone number retrieved from IMS.
+     */
+    @NonNull
+    private final String mNumberFromIms;
+
+    /**
+     * The port index of the Uicc card.
+     */
+    private final int mPortIndex;
+
+    /**
+     * Subscription's preferred usage setting.
+     */
+    @UsageSetting
+    private final int mUsageSetting;
+
+    /**
+     * Last used TP message reference.
+     */
+    private final int mLastUsedTPMessageReference;
+
+    /**
+     * The user id associated with this subscription.
+     */
+    private final int mUserId;
+
+    // Below are the fields that do not exist in the SimInfo table.
+    /**
+     * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
+     */
+    private final int mCardId;
+
+    /**
+     * Whether group of the subscription is disabled. This is only useful if it's a grouped
+     * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
+     * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
+     * this opportunistic subscription. It is intended to use integer to fit the database format.
+     */
+    private final boolean mIsGroupDisabled;
+
+    /**
+     * Constructor from builder.
+     *
+     * @param builder Builder of {@link SubscriptionInfoInternal}.
+     */
+    private SubscriptionInfoInternal(@NonNull Builder builder) {
+        this.mId = builder.mId;
+        this.mIccId = builder.mIccId;
+        this.mSimSlotIndex = builder.mSimSlotIndex;
+        this.mDisplayName = builder.mDisplayName;
+        this.mCarrierName = builder.mCarrierName;
+        this.mDisplayNameSource = builder.mDisplayNameSource;
+        this.mIconTint = builder.mIconTint;
+        this.mNumber = builder.mNumber;
+        this.mDataRoaming = builder.mDataRoaming;
+        this.mMcc = builder.mMcc;
+        this.mMnc = builder.mMnc;
+        this.mEhplmns = builder.mEhplmns;
+        this.mHplmns = builder.mHplmns;
+        this.mIsEmbedded = builder.mIsEmbedded;
+        this.mCardString = builder.mCardString;
+        this.mNativeAccessRules = builder.mNativeAccessRules;
+        this.mCarrierConfigAccessRules = builder.mCarrierConfigAccessRules;
+        this.mIsRemovableEmbedded = builder.mIsRemovableEmbedded;
+        this.mIsEnhanced4GModeEnabled = builder.mIsEnhanced4GModeEnabled;
+        this.mIsVideoTelephonyEnabled = builder.mIsVideoTelephonyEnabled;
+        this.mIsWifiCallingEnabled = builder.mIsWifiCallingEnabled;
+        this.mWifiCallingMode = builder.mWifiCallingMode;
+        this.mWifiCallingModeForRoaming = builder.mWifiCallingModeForRoaming;
+        this.mIsWifiCallingEnabledForRoaming = builder.mIsWifiCallingEnabledForRoaming;
+        this.mIsOpportunistic = builder.mIsOpportunistic;
+        this.mGroupUuid = builder.mGroupUuid;
+        this.mCountryIso = builder.mCountryIso;
+        this.mCarrierId = builder.mCarrierId;
+        this.mProfileClass = builder.mProfileClass;
+        this.mType = builder.mType;
+        this.mGroupOwner = builder.mGroupOwner;
+        this.mEnabledMobileDataPolicies = builder.mEnabledMobileDataPolicies;
+        this.mImsi = builder.mImsi;
+        this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
+        this.mIsRcsUceEnabled = builder.mIsRcsUceEnabled;
+        this.mIsCrossSimCallingEnabled = builder.mIsCrossSimCallingEnabled;
+        this.mRcsConfig = builder.mRcsConfig;
+        this.mAllowedNetworkTypesForReasons = builder.mAllowedNetworkTypesForReasons;
+        this.mDeviceToDeviceStatusSharingPreference =
+                builder.mDeviceToDeviceStatusSharingPreference;
+        this.mIsVoImsOptInEnabled = builder.mIsVoImsOptInEnabled;
+        this.mDeviceToDeviceStatusSharingContacts = builder.mDeviceToDeviceStatusSharingContacts;
+        this.mIsNrAdvancedCallingEnabled = builder.mIsNrAdvancedCallingEnabled;
+        this.mNumberFromCarrier = builder.mNumberFromCarrier;
+        this.mNumberFromIms = builder.mNumberFromIms;
+        this.mPortIndex = builder.mPortIndex;
+        this.mUsageSetting = builder.mUsageSetting;
+        this.mLastUsedTPMessageReference = builder.mLastUsedTPMessageReference;
+        this.mUserId = builder.mUserId;
+
+        // Below are the fields that do not exist in the SimInfo table.
+        this.mCardId = builder.mCardId;
+        this.mIsGroupDisabled = builder.mIsGroupDisabled;
+    }
+
+    /**
+     * @return The subscription ID.
+     */
+    public int getSubscriptionId() {
+        return mId;
+    }
+
+    /**
+     * Returns the ICC ID.
+     *
+     * @return the ICC ID, or an empty string if one of these requirements is not met
+     */
+    @NonNull
+    public String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * @return The index of the SIM slot that currently contains the subscription and not
+     * necessarily unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or
+     * the subscription is inactive.
+     */
+    public int getSimSlotIndex() {
+        return mSimSlotIndex;
+    }
+
+    /**
+     * @return The name displayed to the user that identifies this subscription. This name is
+     * used in Settings page and can be renamed by the user.
+     *
+     * @see #getCarrierName()
+     */
+    @NonNull
+    public CharSequence getDisplayName() {
+        return mDisplayName;
+    }
+
+    /**
+     * @return The name displayed to the user that identifies subscription provider name. This name
+     * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+     *
+     * @see #getDisplayName()
+     */
+    @NonNull
+    public CharSequence getCarrierName() {
+        return mCarrierName;
+    }
+
+    /**
+     * @return The source of the {@link #getDisplayName()}.
+     */
+    @SimDisplayNameSource
+    public int getDisplayNameSource() {
+        return mDisplayNameSource;
+    }
+
+    /**
+     * A highlight color to use in displaying information about this {@code PhoneAccount}.
+     *
+     * @return A hexadecimal color value.
+     */
+    public int getIconTint() {
+        return mIconTint;
+    }
+
+    /**
+     * @return the number of this subscription.
+     */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Whether user enables data roaming for this subscription or not. Either
+     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+     */
+    public int getDataRoaming() {
+        return mDataRoaming;
+    }
+
+    /**
+     * @return The mobile country code.
+     */
+    @NonNull
+    public String getMccString() {
+        return mMcc;
+    }
+
+    /**
+     * @return The mobile network code.
+     */
+    @NonNull
+    public String getMncString() {
+        return mMnc;
+    }
+
+    /**
+     * @return Extended home PLMNs associated with this subscription.
+     */
+    @NonNull
+    public String getEhplmns() {
+        return mEhplmns;
+    }
+
+    /**
+     * @return Home PLMNs associated with this subscription.
+     */
+    @NonNull
+    public String getHplmnsRaw() {
+        return mHplmns;
+    }
+
+    /**
+     * @return {@code true} if the subscription is from eSIM.
+     */
+    public boolean isEmbedded() {
+        return mIsEmbedded != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isEmbedded()}.
+     */
+    public int isEmbeddedRaw() {
+        return mIsEmbedded;
+    }
+
+    /**
+     * Returns the card string of the SIM card which contains the subscription.
+     *
+     * @return The card string of the SIM card which contains the subscription. The card string is
+     * the ICCID for UICCs or the EID for
+     * eUICCs.
+     */
+    @NonNull
+    public String getCardString() {
+        return mCardString;
+    }
+
+    /**
+     * @return The access rules for this subscription, if it is embedded and defines any. This
+     * does not include access rules for non-embedded subscriptions. This is the raw string
+     * stored in the database.
+     */
+    @NonNull
+    public byte[] getNativeAccessRulesRaw() {
+        return mNativeAccessRules;
+    }
+
+    /**
+     * @return The carrier certificates for this subscription that are saved in carrier configs.
+     * This does not include access rules from the Uicc, whether embedded or non-embedded. This
+     * is the raw string stored in the database.
+     */
+    public byte[] getCarrierConfigAccessRulesRaw() {
+        return mCarrierConfigAccessRules;
+    }
+
+    /**
+     * Whether an embedded subscription is on a removable card. Such subscriptions are marked
+     * inaccessible as soon as the current card is removed. Otherwise, they will remain accessible
+     * unless explicitly deleted. Only meaningful when {@link #isEmbedded()} is {@code true}.
+     */
+    public boolean isRemovableEmbedded() {
+        return mIsRemovableEmbedded != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isRemovableEmbedded()}.
+     */
+    public int isRemovableEmbeddedRaw() {
+        return mIsRemovableEmbedded;
+    }
+
+    /**
+     * @return Whether enhanced 4G mode is enabled by the user or not.
+     */
+    public boolean isEnhanced4GModeEnabled() {
+        return mIsEnhanced4GModeEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isEnhanced4GModeEnabled()}.
+     */
+    public int isEnhanced4GModeEnabledRaw() {
+        return mIsEnhanced4GModeEnabled;
+    }
+
+    /**
+     * @return Whether video telephony is enabled by the user or not.
+     */
+    public boolean isVideoTelephonyEnabled() {
+        return mIsVideoTelephonyEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isVideoTelephonyEnabled()}.
+     */
+    public int isVideoTelephonyEnabledRaw() {
+        return mIsVideoTelephonyEnabled;
+    }
+
+    /**
+     * @return Whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+     */
+    public boolean isWifiCallingEnabled() {
+        return mIsWifiCallingEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isWifiCallingEnabled()}.
+     */
+    public int isWifiCallingEnabledRaw() {
+        return mIsWifiCallingEnabled;
+    }
+
+    /**
+     * @return Wi-Fi calling mode when the device is not roaming.
+     */
+    @ImsMmTelManager.WiFiCallingMode
+    public int getWifiCallingMode() {
+        return mWifiCallingMode;
+    }
+
+    /**
+     * @return Whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+     */
+    @ImsMmTelManager.WiFiCallingMode
+    public int getWifiCallingModeForRoaming() {
+        return mWifiCallingModeForRoaming;
+    }
+
+    /**
+     * @return Whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+     */
+    public boolean isWifiCallingEnabledForRoaming() {
+        return mIsWifiCallingEnabledForRoaming != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isWifiCallingEnabledForRoaming()}.
+     */
+    public int isWifiCallingEnabledForRoamingRaw() {
+        return mIsWifiCallingEnabledForRoaming;
+    }
+
+    /**
+     * An opportunistic subscription connects to a network that is
+     * limited in functionality and / or coverage.
+     *
+     * @return Whether subscription is opportunistic.
+     */
+    public boolean isOpportunistic() {
+        return mIsOpportunistic != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isOpportunistic()}.
+     */
+    public int isOpportunisticRaw() {
+        return mIsOpportunistic;
+    }
+
+    /**
+     * Used in scenarios where different subscriptions are bundled as a group.
+     * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()})
+     * Such that those subscriptions will have some affiliated behaviors such as opportunistic
+     * subscription may be invisible to the user.
+     *
+     * @return Group UUID in string format.
+     */
+    @NonNull
+    public String getGroupUuidRaw() {
+        return mGroupUuid;
+    }
+
+    /**
+     * @return The ISO country code. Empty if not available.
+     */
+    public String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * @return The carrier id of this subscription carrier.
+     *
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * @return The profile class populated from the profile metadata if present. Otherwise,
+     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} return
+     * {@code false}).
+     */
+    @ProfileClass
+    public int getProfileClass() {
+        return mProfileClass;
+    }
+
+    /**
+     * This method returns the type of a subscription. It can be
+     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
+     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
+     *
+     * @return The type of the subscription.
+     */
+    @SubscriptionType
+    public int getSubscriptionType() {
+        return mType;
+    }
+
+    /**
+     * @return The owner package of group the subscription belongs to.
+     */
+    @NonNull
+    public String getGroupOwner() {
+        return mGroupOwner;
+    }
+
+    /**
+     * @return The enabled mobile data policies in string format.
+     *
+     * @see com.android.internal.telephony.data.DataSettingsManager#getMobileDataPolicyEnabled
+     */
+    @NonNull
+    public String getEnabledMobileDataPoliciesRaw() {
+        return mEnabledMobileDataPolicies;
+    }
+
+    /**
+     * @return The IMSI (International Mobile Subscriber Identity) of the subscription.
+     */
+    @NonNull
+    public String getImsi() {
+        return mImsi;
+    }
+
+    /**
+     * @return {@code true} if Uicc applications are set to be enabled or disabled.
+     */
+    public boolean areUiccApplicationsEnabled() {
+        return mAreUiccApplicationsEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #areUiccApplicationsEnabled()}.
+     */
+    public int areUiccApplicationsEnabledRaw() {
+        return mAreUiccApplicationsEnabled;
+    }
+
+    /**
+     * @return Whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+     * subscription.
+     */
+    public boolean isRcsUceEnabled() {
+        return mIsRcsUceEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isRcsUceEnabled()}.
+     */
+    public int isRcsUceEnabledRaw() {
+        return mIsRcsUceEnabled;
+    }
+
+    /**
+     * @return Whether the user has enabled cross SIM calling for this subscription.
+     */
+    public boolean isCrossSimCallingEnabled() {
+        return mIsCrossSimCallingEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isCrossSimCallingEnabled()}.
+     */
+    public int isCrossSimCallingEnabledRaw() {
+        return mIsCrossSimCallingEnabled;
+    }
+
+    /**
+     * @return The RCS configuration.
+     */
+    @NonNull
+    public byte[] getRcsConfig() {
+        return mRcsConfig;
+    }
+
+    /**
+     * The allowed network types for reasons in string format. The format is
+     * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+     *
+     * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+     */
+    @NonNull
+    public String getAllowedNetworkTypesForReasons() {
+        return mAllowedNetworkTypesForReasons;
+    }
+
+    /**
+     * @return Device to device sharing status.
+     */
+    @DeviceToDeviceStatusSharingPreference
+    public int getDeviceToDeviceStatusSharingPreference() {
+        return mDeviceToDeviceStatusSharingPreference;
+    }
+
+    /**
+     * @return Whether the user has opted-in voice over IMS.
+     */
+    public boolean isVoImsOptInEnabled() {
+        return mIsVoImsOptInEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isVoImsOptInEnabled()}.
+     */
+    public int isVoImsOptInEnabledRaw() {
+        return mIsVoImsOptInEnabled;
+    }
+
+    /**
+     * @return Contacts information that allow device to device sharing.
+     */
+    @NonNull
+    public String getDeviceToDeviceStatusSharingContacts() {
+        return mDeviceToDeviceStatusSharingContacts;
+    }
+
+    /**
+     * @return Whether the user has enabled NR advanced calling.
+     */
+    public boolean isNrAdvancedCallingEnabled() {
+        return mIsNrAdvancedCallingEnabled != 0;
+    }
+
+    /**
+     * @return The raw database value of {@link #isNrAdvancedCallingEnabled()}.
+     */
+    public int isNrAdvancedCallingEnabledRaw() {
+        return mIsNrAdvancedCallingEnabled;
+    }
+
+    /**
+     * @return Get the phone number retrieved from carrier.
+     */
+    @NonNull
+    public String getNumberFromCarrier() {
+        return mNumberFromCarrier;
+    }
+
+    /**
+     * @return Get the phone number retrieved from IMS.
+     */
+    @NonNull
+    public String getNumberFromIms() {
+        return mNumberFromIms;
+    }
+
+    /**
+     * @return The port index of the SIM card which contains the subscription.
+     */
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * Get the usage setting for this subscription.
+     *
+     * @return The usage setting used for this subscription.
+     */
+    @UsageSetting
+    public int getUsageSetting() {
+        return mUsageSetting;
+    }
+
+    /**
+     * @return Last used TP message reference.
+     */
+    public int getLastUsedTPMessageReference() {
+        return mLastUsedTPMessageReference;
+    }
+
+    /**
+     * @return The user id associated with this subscription.
+     */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    // Below are the fields that do not exist in SimInfo table.
+    /**
+     * @return The card ID of the SIM card which contains the subscription.
+     *
+     * @see android.telephony.UiccCardInfo#getCardId().
+     */
+    public int getCardId() {
+        return mCardId;
+    }
+
+    /**
+     * @return {@code true} if the group of the subscription is disabled. This is only useful if
+     * it's a grouped opportunistic subscription. In this case, if all primary (non-opportunistic)
+     * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we
+     * should disable this opportunistic subscription.
+     */
+    public boolean isGroupDisabled() {
+        return mIsGroupDisabled;
+    }
+
+    /**
+     * Get ID stripped PII information on user build.
+     *
+     * @param id The PII id.
+     *
+     * @return The stripped string.
+     */
+    public static String givePrintableId(String id) {
+        String idToPrint = null;
+        if (id != null) {
+            int len = id.length();
+            if (len > 6 && !TelephonyUtils.IS_DEBUGGABLE) {
+                idToPrint = id.substring(0, len - 6) + Rlog.pii(false, id.substring(len - 6));
+            } else {
+                idToPrint = id;
+            }
+        }
+        return idToPrint;
+    }
+
+    @Override
+    public String toString() {
+        return "[SubscriptionInfoInternal: id=" + mId
+                + " iccId=" + givePrintableId(mIccId)
+                + " simSlotIndex=" + mSimSlotIndex
+                + " portIndex=" + mPortIndex
+                + " isEmbedded=" + mIsEmbedded
+                + " isRemovableEmbedded=" + mIsRemovableEmbedded
+                + " carrierId=" + mCarrierId
+                + " displayName=" + mDisplayName
+                + " carrierName=" + mCarrierName
+                + " isOpportunistic=" + mIsOpportunistic
+                + " groupUuid=" + mGroupUuid
+                + " groupOwner=" + mGroupOwner
+                + " displayNameSource="
+                + SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
+                + " iconTint=" + mIconTint
+                + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+                + " dataRoaming=" + mDataRoaming
+                + " mcc=" + mMcc
+                + " mnc=" + mMnc
+                + " ehplmns=" + mEhplmns
+                + " hplmns=" + mHplmns
+                + " cardString=" + givePrintableId(mCardString)
+                + " cardId=" + mCardId
+                + " nativeAccessRules=" + IccUtils.bytesToHexString(mNativeAccessRules)
+                + " carrierConfigAccessRules=" + IccUtils.bytesToHexString(
+                        mCarrierConfigAccessRules)
+                + " countryIso=" + mCountryIso
+                + " profileClass=" + mProfileClass
+                + " type=" + SubscriptionManager.subscriptionTypeToString(mType)
+                + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+                + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+                + " isEnhanced4GModeEnabled=" + mIsEnhanced4GModeEnabled
+                + " isVideoTelephonyEnabled=" + mIsVideoTelephonyEnabled
+                + " isWifiCallingEnabled=" + mIsWifiCallingEnabled
+                + " isWifiCallingEnabledForRoaming=" + mIsWifiCallingEnabledForRoaming
+                + " wifiCallingMode=" + ImsMmTelManager.wifiCallingModeToString(mWifiCallingMode)
+                + " wifiCallingModeForRoaming="
+                + ImsMmTelManager.wifiCallingModeToString(mWifiCallingModeForRoaming)
+                + " enabledMobileDataPolicies=" + mEnabledMobileDataPolicies
+                + " imsi=" + givePrintableId(mImsi)
+                + " rcsUceEnabled=" + mIsRcsUceEnabled
+                + " crossSimCallingEnabled=" + mIsCrossSimCallingEnabled
+                + " rcsConfig=" + IccUtils.bytesToHexString(mRcsConfig)
+                + " allowedNetworkTypesForReasons=" + mAllowedNetworkTypesForReasons
+                + " deviceToDeviceStatusSharingPreference=" + mDeviceToDeviceStatusSharingPreference
+                + " isVoImsOptInEnabled=" + mIsVoImsOptInEnabled
+                + " deviceToDeviceStatusSharingContacts=" + mDeviceToDeviceStatusSharingContacts
+                + " numberFromCarrier=" + mNumberFromCarrier
+                + " numberFromIms=" + mNumberFromIms
+                + " userId=" + mUserId
+                + " isGroupDisabled=" + mIsGroupDisabled
+                + "]";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+        return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
+                && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
+                && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
+                && mIsRemovableEmbedded == that.mIsRemovableEmbedded
+                && mIsEnhanced4GModeEnabled == that.mIsEnhanced4GModeEnabled
+                && mIsVideoTelephonyEnabled == that.mIsVideoTelephonyEnabled
+                && mIsWifiCallingEnabled == that.mIsWifiCallingEnabled
+                && mWifiCallingMode == that.mWifiCallingMode
+                && mWifiCallingModeForRoaming == that.mWifiCallingModeForRoaming
+                && mIsWifiCallingEnabledForRoaming == that.mIsWifiCallingEnabledForRoaming
+                && mIsOpportunistic == that.mIsOpportunistic && mCarrierId == that.mCarrierId
+                && mProfileClass == that.mProfileClass && mType == that.mType
+                && mAreUiccApplicationsEnabled == that.mAreUiccApplicationsEnabled
+                && mIsRcsUceEnabled == that.mIsRcsUceEnabled
+                && mIsCrossSimCallingEnabled == that.mIsCrossSimCallingEnabled
+                && mDeviceToDeviceStatusSharingPreference
+                == that.mDeviceToDeviceStatusSharingPreference
+                && mIsVoImsOptInEnabled == that.mIsVoImsOptInEnabled
+                && mIsNrAdvancedCallingEnabled == that.mIsNrAdvancedCallingEnabled
+                && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
+                && mLastUsedTPMessageReference == that.mLastUsedTPMessageReference
+                && mUserId == that.mUserId && mCardId == that.mCardId
+                && mIsGroupDisabled == that.mIsGroupDisabled && mIccId.equals(that.mIccId)
+                && mDisplayName.equals(that.mDisplayName) && mCarrierName.equals(that.mCarrierName)
+                && mNumber.equals(that.mNumber) && mMcc.equals(that.mMcc) && mMnc.equals(that.mMnc)
+                && mEhplmns.equals(that.mEhplmns) && mHplmns.equals(that.mHplmns)
+                && mCardString.equals(
+                that.mCardString) && Arrays.equals(mNativeAccessRules,
+                that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
+                that.mCarrierConfigAccessRules) && mGroupUuid.equals(that.mGroupUuid)
+                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
+                && mEnabledMobileDataPolicies.equals(that.mEnabledMobileDataPolicies)
+                && mImsi.equals(
+                that.mImsi) && Arrays.equals(mRcsConfig, that.mRcsConfig)
+                && mAllowedNetworkTypesForReasons.equals(that.mAllowedNetworkTypesForReasons)
+                && mDeviceToDeviceStatusSharingContacts.equals(
+                that.mDeviceToDeviceStatusSharingContacts) && mNumberFromCarrier.equals(
+                that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
+                mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mEhplmns, mHplmns,
+                mIsEmbedded, mCardString, mIsRemovableEmbedded, mIsEnhanced4GModeEnabled,
+                mIsVideoTelephonyEnabled, mIsWifiCallingEnabled, mWifiCallingMode,
+                mWifiCallingModeForRoaming, mIsWifiCallingEnabledForRoaming, mIsOpportunistic,
+                mGroupUuid, mCountryIso, mCarrierId, mProfileClass, mType, mGroupOwner,
+                mEnabledMobileDataPolicies, mImsi, mAreUiccApplicationsEnabled, mIsRcsUceEnabled,
+                mIsCrossSimCallingEnabled, mAllowedNetworkTypesForReasons,
+                mDeviceToDeviceStatusSharingPreference, mIsVoImsOptInEnabled,
+                mDeviceToDeviceStatusSharingContacts, mIsNrAdvancedCallingEnabled,
+                mNumberFromCarrier,
+                mNumberFromIms, mPortIndex, mUsageSetting, mLastUsedTPMessageReference, mUserId,
+                mCardId, mIsGroupDisabled);
+        result = 31 * result + Arrays.hashCode(mNativeAccessRules);
+        result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
+        result = 31 * result + Arrays.hashCode(mRcsConfig);
+        return result;
+    }
+
+    /**
+     * The builder class of {@link SubscriptionInfoInternal}.
+     */
+    public static class Builder {
+        /**
+         * The subscription id.
+         */
+        private int mId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+        /**
+         * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+         */
+        @NonNull
+        private String mIccId = "";
+
+        /**
+         * The index of the SIM slot that currently contains the subscription and not necessarily
+         * unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the
+         * subscription is inactive.
+         */
+        private int mSimSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+
+        /**
+         * The name displayed to the user that identifies this subscription. This name is used
+         * in Settings page and can be renamed by the user.
+         */
+        @NonNull
+        private String mDisplayName = "";
+
+        /**
+         * The name displayed to the user that identifies subscription provider name. This name
+         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+         */
+        @NonNull
+        private String mCarrierName = "";
+
+        /**
+         * The source of the display name.
+         */
+        @SimDisplayNameSource
+        private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_UNKNOWN;
+
+        /**
+         * The color to be used for tinting the icon when displaying to the user.
+         */
+        private int mIconTint = 0;
+
+        /**
+         * The number presented to the user identify this subscription.
+         */
+        @NonNull
+        private String mNumber = "";
+
+        /**
+         * Whether user enables data roaming for this subscription or not. Either
+         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+         */
+        private int mDataRoaming = SubscriptionManager.DATA_ROAMING_DISABLE;
+
+        /**
+         * The mobile country code.
+         */
+        @NonNull
+        private String mMcc = "";
+
+        /**
+         * The mobile network code.
+         */
+        @NonNull
+        private String mMnc = "";
+
+        /**
+         * EHPLMNs associated with the subscription.
+         */
+        @NonNull
+        private String mEhplmns = "";
+
+        /**
+         * HPLMNs associated with the subscription.
+         */
+        @NonNull
+        private String mHplmns = "";
+
+        /**
+         * Whether the subscription is from eSIM.
+         */
+        private int mIsEmbedded = 0;
+
+        /**
+         * The card string of the SIM card.
+         */
+        @NonNull
+        private String mCardString = "";
+
+        /**
+         * The native access rules for this subscription, if it is embedded and defines any. This
+         * does not include access rules for non-embedded subscriptions.
+         */
+        @NonNull
+        private byte[] mNativeAccessRules = new byte[0];
+
+        /**
+         * The carrier certificates for this subscription that are saved in carrier configs.
+         * This does not include access rules from the Uicc, whether embedded or non-embedded.
+         */
+        @NonNull
+        private byte[] mCarrierConfigAccessRules = new byte[0];
+
+        /**
+         * Whether an embedded subscription is on a removable card. Such subscriptions are marked
+         * inaccessible as soon as the current card is removed. Otherwise, they will remain
+         * accessible unless explicitly deleted. Only meaningful when {@link #isEmbedded()} is
+         * {@code true}.
+         */
+        private int mIsRemovableEmbedded = 0;
+
+        /**
+         * Whether enhanced 4G mode is enabled by the user or not.
+         */
+        private int mIsEnhanced4GModeEnabled = 0;
+
+        /**
+         * Whether video telephony is enabled by the user or not.
+         */
+        private int mIsVideoTelephonyEnabled = 0;
+
+        /**
+         * Whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+         */
+        private int mIsWifiCallingEnabled = 0;
+
+        /**
+         * Wi-Fi calling mode when the device is not roaming.
+         */
+        @ImsMmTelManager.WiFiCallingMode
+        private int mWifiCallingMode = ImsMmTelManager.WIFI_MODE_UNKNOWN;
+
+        /**
+         * Wi-Fi calling mode when the device is roaming.
+         */
+        @ImsMmTelManager.WiFiCallingMode
+        private int mWifiCallingModeForRoaming = ImsMmTelManager.WIFI_MODE_UNKNOWN;
+
+        /**
+         * Whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+         */
+        private int mIsWifiCallingEnabledForRoaming = 0;
+
+        /**
+         * Whether the subscription is opportunistic or not.
+         */
+        private int mIsOpportunistic = 0;
+
+        /**
+         * The group UUID of the subscription group in string format.
+         */
+        @NonNull
+        private String mGroupUuid = "";
+
+        /**
+         * The ISO Country code for the subscription's provider.
+         */
+        @NonNull
+        private String mCountryIso = "";
+
+        /**
+         * The carrier id.
+         *
+         * @see TelephonyManager#getSimCarrierId()
+         */
+        private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+        /**
+         * The profile class populated from the profile metadata if present. Otherwise, the profile
+         * class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no profile
+         * metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+         * {@code false}).
+         */
+        @ProfileClass
+        private int mProfileClass = SubscriptionManager.PROFILE_CLASS_UNSET;
+
+        /**
+         * The subscription type.
+         */
+        @SubscriptionType
+        private int mType = SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM;
+
+        /**
+         * The owner package of group the subscription belongs to.
+         */
+        @NonNull
+        private String mGroupOwner = "";
+
+        /**
+         * The enabled mobile data policies in string format.
+         */
+        @NonNull
+        private String mEnabledMobileDataPolicies = "";
+
+        /**
+         * The IMSI (International Mobile Subscriber Identity) of the subscription.
+         */
+        @NonNull
+        private String mImsi = "";
+
+        /**
+         * Whether Uicc applications are configured to enable or not.
+         */
+        private int mAreUiccApplicationsEnabled = 0;
+
+        /**
+         * Whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+         * subscription.
+         */
+        private int mIsRcsUceEnabled = 0;
+
+        /**
+         * Whether the user has enabled cross SIM calling for this subscription.
+         */
+        private int mIsCrossSimCallingEnabled = 0;
+
+        /**
+         * The RCS configuration.
+         */
+        private byte[] mRcsConfig = new byte[0];
+
+        /**
+         * The allowed network types for reasons in string format. The format is
+         * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+         *
+         * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+         */
+        private String mAllowedNetworkTypesForReasons = "";
+
+        /**
+         * Device to device sharing status.
+         */
+        @DeviceToDeviceStatusSharingPreference
+        private int mDeviceToDeviceStatusSharingPreference =
+                SubscriptionManager.D2D_SHARING_DISABLED;
+
+        /**
+         * Whether the user has opted-in voice over IMS.
+         */
+        private int mIsVoImsOptInEnabled = 0;
+
+        /**
+         * Contacts information that allow device to device sharing.
+         */
+        @NonNull
+        private String mDeviceToDeviceStatusSharingContacts = "";
+
+        /**
+         * Whether the user has enabled NR advanced calling.
+         */
+        private int mIsNrAdvancedCallingEnabled = 0;
+
+        /**
+         * The phone number retrieved from carrier.
+         */
+        @NonNull
+        private String mNumberFromCarrier = "";
+
+        /**
+         * The phone number retrieved from IMS.
+         */
+        @NonNull
+        private String mNumberFromIms = "";
+
+        /**
+         * the port index of the Uicc card.
+         */
+        private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
+
+        /**
+         * Subscription's preferred usage setting.
+         */
+        @UsageSetting
+        private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+        /**
+         * Last used TP message reference.
+         */
+        private int mLastUsedTPMessageReference = -1;
+
+        /**
+         * The user id associated with this subscription.
+         */
+        private int mUserId = UserHandle.USER_NULL;
+
+        // The following fields do not exist in the SimInfo table.
+        /**
+         * The card ID of the SIM card which contains the subscription.
+         */
+        private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
+
+        /**
+         * Whether group of the subscription is disabled. This is only useful if it's a grouped
+         * opportunistic subscription. In this case, if all primary (non-opportunistic)
+         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+         * we should disable this opportunistic subscription.
+         */
+        private boolean mIsGroupDisabled;
+
+        /**
+         * Default constructor.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructor from {@link SubscriptionInfoInternal}.
+         *
+         * @param info The subscription info.
+         */
+        public Builder(@NonNull SubscriptionInfoInternal info) {
+            mId = info.mId;
+            mIccId = info.mIccId;
+            mSimSlotIndex = info.mSimSlotIndex;
+            mDisplayName = info.mDisplayName;
+            mCarrierName = info.mCarrierName;
+            mDisplayNameSource = info.mDisplayNameSource;
+            mIconTint = info.mIconTint;
+            mNumber = info.mNumber;
+            mDataRoaming = info.mDataRoaming;
+            mMcc = info.mMcc;
+            mMnc = info.mMnc;
+            mEhplmns = info.mEhplmns;
+            mHplmns = info.mHplmns;
+            mIsEmbedded = info.mIsEmbedded;
+            mCardString = info.mCardString;
+            mNativeAccessRules = info.mNativeAccessRules;
+            mCarrierConfigAccessRules = info.mCarrierConfigAccessRules;
+            mIsRemovableEmbedded = info.mIsRemovableEmbedded;
+            mIsEnhanced4GModeEnabled = info.mIsEnhanced4GModeEnabled;
+            mIsVideoTelephonyEnabled = info.mIsVideoTelephonyEnabled;
+            mIsWifiCallingEnabled = info.mIsWifiCallingEnabled;
+            mWifiCallingMode = info.mWifiCallingMode;
+            mWifiCallingModeForRoaming = info.mWifiCallingModeForRoaming;
+            mIsWifiCallingEnabledForRoaming = info.mIsWifiCallingEnabledForRoaming;
+            mIsOpportunistic = info.mIsOpportunistic;
+            mGroupUuid = info.mGroupUuid;
+            mCountryIso = info.mCountryIso;
+            mCarrierId = info.mCarrierId;
+            mProfileClass = info.mProfileClass;
+            mType = info.mType;
+            mGroupOwner = info.mGroupOwner;
+            mEnabledMobileDataPolicies = info.mEnabledMobileDataPolicies;
+            mImsi = info.mImsi;
+            mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
+            mIsRcsUceEnabled = info.mIsRcsUceEnabled;
+            mIsCrossSimCallingEnabled = info.mIsCrossSimCallingEnabled;
+            mRcsConfig = info.mRcsConfig;
+            mAllowedNetworkTypesForReasons = info.mAllowedNetworkTypesForReasons;
+            mDeviceToDeviceStatusSharingPreference = info.mDeviceToDeviceStatusSharingPreference;
+            mIsVoImsOptInEnabled = info.mIsVoImsOptInEnabled;
+            mDeviceToDeviceStatusSharingContacts = info.mDeviceToDeviceStatusSharingContacts;
+            mIsNrAdvancedCallingEnabled = info.mIsNrAdvancedCallingEnabled;
+            mNumberFromCarrier = info.mNumberFromCarrier;
+            mNumberFromIms = info.mNumberFromIms;
+            mPortIndex = info.mPortIndex;
+            mUsageSetting = info.mUsageSetting;
+            mLastUsedTPMessageReference = info.getLastUsedTPMessageReference();
+            mUserId = info.mUserId;
+            // Below are the fields that do not exist in the SimInfo table.
+            mCardId = info.mCardId;
+            mIsGroupDisabled = info.mIsGroupDisabled;
+        }
+
+        /**
+         * Set the subscription id.
+         *
+         * @param id The subscription id.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setId(int id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set the ICCID of the SIM that is associated with this subscription.
+         *
+         * @param iccId The ICCID of the SIM that is associated with this subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIccId(@NonNull String iccId) {
+            Objects.requireNonNull(iccId);
+            mIccId = iccId;
+            return this;
+        }
+
+        /**
+         * Set the SIM index of the slot that currently contains the subscription. Set to
+         * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+         *
+         * @param simSlotIndex The SIM slot index.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setSimSlotIndex(int simSlotIndex) {
+            mSimSlotIndex = simSlotIndex;
+            return this;
+        }
+
+        /**
+         * The name displayed to the user that identifies this subscription. This name is used
+         * in Settings page and can be renamed by the user.
+         *
+         * @param displayName The display name.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setDisplayName(@NonNull String displayName) {
+            Objects.requireNonNull(displayName);
+            mDisplayName = displayName;
+            return this;
+        }
+
+        /**
+         * The name displayed to the user that identifies subscription provider name. This name
+         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+         *
+         * @param carrierName The carrier name.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCarrierName(@NonNull String carrierName) {
+            Objects.requireNonNull(carrierName);
+            mCarrierName = carrierName;
+            return this;
+        }
+
+        /**
+         * Set the source of the display name.
+         *
+         * @param displayNameSource The source of the display name.
+         * @return The builder.
+         *
+         * @see SubscriptionInfoInternal#getDisplayName()
+         */
+        @NonNull
+        public Builder setDisplayNameSource(@SimDisplayNameSource int displayNameSource) {
+            mDisplayNameSource = displayNameSource;
+            return this;
+        }
+
+        /**
+         * Set the color to be used for tinting the icon when displaying to the user.
+         *
+         * @param iconTint The color to be used for tinting the icon when displaying to the user.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIconTint(int iconTint) {
+            mIconTint = iconTint;
+            return this;
+        }
+
+        /**
+         * Set the number presented to the user identify this subscription.
+         *
+         * @param number the number presented to the user identify this subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNumber(@NonNull String number) {
+            Objects.requireNonNull(number);
+            mNumber = number;
+            return this;
+        }
+
+        /**
+         * Set whether user enables data roaming for this subscription or not.
+         *
+         * @param dataRoaming Data roaming mode. Either
+         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setDataRoaming(int dataRoaming) {
+            mDataRoaming = dataRoaming;
+            return this;
+        }
+
+        /**
+         * Set the mobile country code.
+         *
+         * @param mcc The mobile country code.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setMcc(@NonNull String mcc) {
+            Objects.requireNonNull(mcc);
+            mMcc = mcc;
+            return this;
+        }
+
+        /**
+         * Set the mobile network code.
+         *
+         * @param mnc Mobile network code.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setMnc(@NonNull String mnc) {
+            Objects.requireNonNull(mnc);
+            mMnc = mnc;
+            return this;
+        }
+
+        /**
+         * Set EHPLMNs associated with the subscription.
+         *
+         * @param ehplmns EHPLMNs associated with the subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEhplmns(@NonNull String ehplmns) {
+            Objects.requireNonNull(ehplmns);
+            mEhplmns = ehplmns;
+            return this;
+        }
+
+        /**
+         * Set HPLMNs associated with the subscription.
+         *
+         * @param hplmns HPLMNs associated with the subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setHplmns(@NonNull String hplmns) {
+            Objects.requireNonNull(hplmns);
+            mHplmns = hplmns;
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is from eSIM or not.
+         *
+         * @param isEmbedded {@code true} if the subscription is from eSIM.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEmbedded(int isEmbedded) {
+            mIsEmbedded = isEmbedded;
+            return this;
+        }
+
+        /**
+         * Set the card string of the SIM card.
+         *
+         * @param cardString The card string of the SIM card.
+         *
+         * @return The builder.
+         *
+         * @see #getCardString()
+         */
+        @NonNull
+        public Builder setCardString(@NonNull String cardString) {
+            Objects.requireNonNull(cardString);
+            mCardString = cardString;
+            return this;
+        }
+
+        /**
+         * Set the native access rules for this subscription, if it is embedded and defines any.
+         * This does not include access rules for non-embedded subscriptions.
+         *
+         * @param nativeAccessRules The native access rules for this subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNativeAccessRules(@NonNull byte[] nativeAccessRules) {
+            Objects.requireNonNull(nativeAccessRules);
+            mNativeAccessRules = nativeAccessRules;
+            return this;
+        }
+
+        /**
+         * Set the carrier certificates for this subscription that are saved in carrier configs.
+         * This does not include access rules from the Uicc, whether embedded or non-embedded.
+         *
+         * @param carrierConfigAccessRules The carrier certificates for this subscription.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCarrierConfigAccessRules(@NonNull byte[] carrierConfigAccessRules) {
+            Objects.requireNonNull(carrierConfigAccessRules);
+            mCarrierConfigAccessRules = carrierConfigAccessRules;
+            return this;
+        }
+
+        /**
+         * Set whether an embedded subscription is on a removable card. Such subscriptions are
+         * marked inaccessible as soon as the current card is removed. Otherwise, they will remain
+         * accessible unless explicitly deleted. Only meaningful when {@link #isEmbedded()} is
+         * {@code true}.
+         *
+         * @param isRemovableEmbedded {@code true} if the subscription is from the removable
+         * embedded SIM.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setRemovableEmbedded(int isRemovableEmbedded) {
+            mIsRemovableEmbedded = isRemovableEmbedded;
+            return this;
+        }
+
+        /**
+         * Set whether enhanced 4G mode is enabled by the user or not.
+         *
+         * @param isEnhanced4GModeEnabled whether enhanced 4G mode is enabled by the user or not.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEnhanced4GModeEnabled(int isEnhanced4GModeEnabled) {
+            mIsEnhanced4GModeEnabled = isEnhanced4GModeEnabled;
+            return this;
+        }
+
+        /**
+         * Set whether video telephony is enabled by the user or not.
+         *
+         * @param isVideoTelephonyEnabled whether video telephony is enabled by the user or not.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setVideoTelephonyEnabled(int isVideoTelephonyEnabled) {
+            mIsVideoTelephonyEnabled = isVideoTelephonyEnabled;
+            return this;
+        }
+
+        /**
+         * Set whether Wi-Fi calling is enabled by the user or not when the device is not roaming.
+         *
+         * @param isWifiCallingEnabled whether Wi-Fi calling is enabled by the user or not when
+         * the device is not roaming.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setWifiCallingEnabled(int isWifiCallingEnabled) {
+            mIsWifiCallingEnabled = isWifiCallingEnabled;
+            return this;
+        }
+
+        /**
+         * Set Wi-Fi calling mode when the device is not roaming.
+         *
+         * @param wifiCallingMode Wi-Fi calling mode when the device is not roaming.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setWifiCallingMode(@ImsMmTelManager.WiFiCallingMode int wifiCallingMode) {
+            mWifiCallingMode = wifiCallingMode;
+            return this;
+        }
+
+        /**
+         * Set Wi-Fi calling mode when the device is roaming.
+         *
+         * @param wifiCallingModeForRoaming Wi-Fi calling mode when the device is roaming.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setWifiCallingModeForRoaming(
+                @ImsMmTelManager.WiFiCallingMode int wifiCallingModeForRoaming) {
+            mWifiCallingModeForRoaming = wifiCallingModeForRoaming;
+            return this;
+        }
+
+        /**
+         * Set whether Wi-Fi calling is enabled by the user or not when the device is roaming.
+         *
+         * @param wifiCallingEnabledForRoaming whether Wi-Fi calling is enabled by the user or not
+         * when the device is roaming.
+         *
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setWifiCallingEnabledForRoaming(int wifiCallingEnabledForRoaming) {
+            mIsWifiCallingEnabledForRoaming = wifiCallingEnabledForRoaming;
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is opportunistic or not.
+         *
+         * @param isOpportunistic {@code true} if the subscription is opportunistic.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setOpportunistic(int isOpportunistic) {
+            mIsOpportunistic = isOpportunistic;
+            return this;
+        }
+
+        /**
+         * Set the group UUID of the subscription group.
+         *
+         * @param groupUuid The group UUID.
+         * @return The builder.
+         *
+         */
+        @NonNull
+        public Builder setGroupUuid(@NonNull String groupUuid) {
+            Objects.requireNonNull(groupUuid);
+            mGroupUuid = groupUuid;
+            return this;
+        }
+
+        /**
+         * Set the ISO country code for the subscription's provider.
+         *
+         * @param countryIso The ISO country code for the subscription's provider.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCountryIso(@NonNull String countryIso) {
+            Objects.requireNonNull(countryIso);
+            mCountryIso = countryIso;
+            return this;
+        }
+
+        /**
+         * Set the subscription carrier id.
+         *
+         * @param carrierId The carrier id.
+         * @return The builder
+         *
+         * @see TelephonyManager#getSimCarrierId()
+         */
+        @NonNull
+        public Builder setCarrierId(int carrierId) {
+            mCarrierId = carrierId;
+            return this;
+        }
+
+        /**
+         * Set the profile class populated from the profile metadata if present.
+         *
+         * @param profileClass the profile class populated from the profile metadata if present.
+         * @return The builder
+         *
+         * @see #getProfileClass()
+         */
+        @NonNull
+        public Builder setProfileClass(@ProfileClass int profileClass) {
+            mProfileClass = profileClass;
+            return this;
+        }
+
+        /**
+         * Set the subscription type.
+         *
+         * @param type Subscription type.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setType(@SubscriptionType int type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Set the owner package of group the subscription belongs to.
+         *
+         * @param groupOwner Owner package of group the subscription belongs to.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setGroupOwner(@NonNull String groupOwner) {
+            Objects.requireNonNull(groupOwner);
+            mGroupOwner = groupOwner;
+            return this;
+        }
+
+        /**
+         * Set the enabled mobile data policies.
+         *
+         * @param enabledMobileDataPolicies The enabled mobile data policies.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEnabledMobileDataPolicies(@NonNull String enabledMobileDataPolicies) {
+            Objects.requireNonNull(enabledMobileDataPolicies);
+            mEnabledMobileDataPolicies = enabledMobileDataPolicies;
+            return this;
+        }
+
+        /**
+         * Set the IMSI (International Mobile Subscriber Identity) of the subscription.
+         *
+         * @param imsi The IMSI.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setImsi(@NonNull String imsi) {
+            Objects.requireNonNull(imsi);
+            mImsi = imsi;
+            return this;
+        }
+
+        /**
+         * Set whether Uicc applications are configured to enable or not.
+         *
+         * @param areUiccApplicationsEnabled {@code true} if Uicc applications are configured to
+         * enable.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setUiccApplicationsEnabled(int areUiccApplicationsEnabled) {
+            mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
+            return this;
+        }
+
+        /**
+         * Set whether the user has enabled IMS RCS User Capability Exchange (UCE) for this
+         * subscription.
+         *
+         * @param isRcsUceEnabled If the user enabled RCS UCE for this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setRcsUceEnabled(int isRcsUceEnabled) {
+            mIsRcsUceEnabled = isRcsUceEnabled;
+            return this;
+        }
+
+        /**
+         * Set whether the user has enabled cross SIM calling for this subscription.
+         *
+         * @param isCrossSimCallingEnabled If the user enabled cross SIM calling for this
+         * subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCrossSimCallingEnabled(int isCrossSimCallingEnabled) {
+            mIsCrossSimCallingEnabled = isCrossSimCallingEnabled;
+            return this;
+        }
+
+        /**
+         * Set the RCS config for this subscription.
+         *
+         * @param rcsConfig The RCS config for this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setRcsConfig(byte[] rcsConfig) {
+            Objects.requireNonNull(rcsConfig);
+            mRcsConfig = rcsConfig;
+            return this;
+        }
+
+        /**
+         * Set the allowed network types for reasons.
+         *
+         * @param allowedNetworkTypesForReasons The allowed network types for reasons in string
+         * format. The format is
+         * "[reason]=[network types bitmask], [reason]=[network types bitmask], ..."
+         *
+         * For example, "user=1239287394, thermal=298791239, carrier=3456812312".
+         *
+         * @return The builder.
+         */
+        public Builder setAllowedNetworkTypesForReasons(
+                @NonNull String allowedNetworkTypesForReasons) {
+            Objects.requireNonNull(allowedNetworkTypesForReasons);
+            mAllowedNetworkTypesForReasons = allowedNetworkTypesForReasons;
+            return this;
+        }
+
+        /**
+         * Set device to device sharing status.
+         *
+         * @param deviceToDeviceStatusSharingPreference Device to device sharing status.
+         * @return The builder.
+         */
+        public Builder setDeviceToDeviceStatusSharingPreference(
+                @DeviceToDeviceStatusSharingPreference int deviceToDeviceStatusSharingPreference) {
+            mDeviceToDeviceStatusSharingPreference = deviceToDeviceStatusSharingPreference;
+            return this;
+        }
+
+        /**
+         * Set whether the user has opted-in voice over IMS.
+         *
+         * @param isVoImsOptInEnabled Whether the user has opted-in voice over IMS.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setVoImsOptInEnabled(int isVoImsOptInEnabled) {
+            mIsVoImsOptInEnabled = isVoImsOptInEnabled;
+            return this;
+        }
+
+        /**
+         * Set contacts information that allow device to device sharing.
+         *
+         * @param deviceToDeviceStatusSharingContacts contacts information that allow device to
+         * device sharing.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setDeviceToDeviceStatusSharingContacts(
+                @NonNull String deviceToDeviceStatusSharingContacts) {
+            Objects.requireNonNull(deviceToDeviceStatusSharingContacts);
+            mDeviceToDeviceStatusSharingContacts = deviceToDeviceStatusSharingContacts;
+            return this;
+        }
+
+        /**
+         * Set whether the user has enabled NR advanced calling.
+         *
+         * @param isNrAdvancedCallingEnabled Whether the user has enabled NR advanced calling.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNrAdvancedCallingEnabled(int isNrAdvancedCallingEnabled) {
+            mIsNrAdvancedCallingEnabled = isNrAdvancedCallingEnabled;
+            return this;
+        }
+
+        /**
+         * Set the phone number retrieved from carrier.
+         *
+         * @param numberFromCarrier The phone number retrieved from carrier.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNumberFromCarrier(@NonNull String numberFromCarrier) {
+            Objects.requireNonNull(numberFromCarrier);
+            mNumberFromCarrier = numberFromCarrier;
+            return this;
+        }
+
+        /**
+         * Set the phone number retrieved from IMS.
+         *
+         * @param numberFromIms The phone number retrieved from IMS.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNumberFromIms(@NonNull String numberFromIms) {
+            Objects.requireNonNull(numberFromIms);
+            mNumberFromIms = numberFromIms;
+            return this;
+        }
+
+        /**
+         * Set the port index of the Uicc card.
+         *
+         * @param portIndex The port index of the Uicc card.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setPortIndex(int portIndex) {
+            mPortIndex = portIndex;
+            return this;
+        }
+
+        /**
+         * Set subscription's preferred usage setting.
+         *
+         * @param usageSetting Subscription's preferred usage setting.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setUsageSetting(@UsageSetting int usageSetting) {
+            mUsageSetting = usageSetting;
+            return this;
+        }
+
+        /**
+         * Set last used TP message reference.
+         *
+         * @param lastUsedTPMessageReference Last used TP message reference.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setLastUsedTPMessageReference(
+                int lastUsedTPMessageReference) {
+            mLastUsedTPMessageReference = lastUsedTPMessageReference;
+            return this;
+        }
+
+        /**
+         * Set the user id associated with this subscription.
+         *
+         * @param userId The user id associated with this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        // Below are the fields that do not exist in the SimInfo table.
+        /**
+         * Set the card ID of the SIM card which contains the subscription.
+         *
+         * @param cardId The card ID of the SIM card which contains the subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCardId(int cardId) {
+            mCardId = cardId;
+            return this;
+        }
+
+        /**
+         * Whether group of the subscription is disabled. This is only useful if it's a grouped
+         * opportunistic subscription. In this case, if all primary (non-opportunistic)
+         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+         * we should disable this opportunistic subscription.
+         *
+         * @param isGroupDisabled {@code true} if group of the subscription is disabled.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setGroupDisabled(boolean isGroupDisabled) {
+            mIsGroupDisabled = isGroupDisabled;
+            return this;
+        }
+
+        /**
+         * Build the {@link SubscriptionInfoInternal}.
+         *
+         * @return The {@link SubscriptionInfoInternal} instance.
+         */
+        public SubscriptionInfoInternal build() {
+            return new SubscriptionInfoInternal(this);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
new file mode 100644
index 0000000..e0b5589
--- /dev/null
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -0,0 +1,636 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.ParcelUuid;
+import android.os.TelephonyServiceManager;
+import android.os.UserHandle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISub;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The subscription manager service is the backend service of {@link SubscriptionManager}.
+ * The service handles all SIM subscription related requests from clients.
+ */
+public class SubscriptionManagerService extends ISub.Stub {
+    private static final String LOG_TAG = "SMSVC";
+
+    /** Whether enabling verbose debugging message or not. */
+    private static final boolean VDBG = false;
+
+    /** The context */
+    private final Context mContext;
+
+    /** Local log for most important debug messages. */
+    private final LocalLog mLocalLog = new LocalLog(128);
+
+    /** The subscription database manager. */
+    private final SubscriptionDatabaseManager mSubscriptionDatabaseManager;
+
+    /**
+     * The constructor
+     *
+     * @param context The context
+     */
+    public SubscriptionManagerService(@NonNull Context context) {
+        mContext = context;
+        TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer =
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getSubscriptionServiceRegisterer();
+        subscriptionServiceRegisterer.register(this);
+
+        // Create a separate thread for subscription database manager. The database will be updated
+        // from a different thread.
+        HandlerThread handlerThread = new HandlerThread(LOG_TAG);
+        handlerThread.start();
+        mSubscriptionDatabaseManager = new SubscriptionDatabaseManager(context,
+                handlerThread.getLooper());
+    }
+
+    /**
+     * @param callingPackage The package making the call.
+     * @param callingFeatureId The feature in the package
+     * @return a list of all subscriptions in the database, this includes
+     * all subscriptions that have been seen.
+     */
+    @Override
+    public List<SubscriptionInfo> getAllSubInfoList(@NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * Get the active {@link SubscriptionInfo} with the subscription id key.
+     *
+     * @param subId The unique {@link SubscriptionInfo} key in database
+     * @param callingPackage The package making the call
+     * @param callingFeatureId The feature in the package
+     *
+     * @return The subscription info.
+     */
+    @Override
+    @Nullable
+    public SubscriptionInfo getActiveSubscriptionInfo(int subId, @NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * Get the active {@link SubscriptionInfo} associated with the iccId.
+     *
+     * @param iccId the IccId of SIM card
+     * @param callingPackage The package making the call
+     * @param callingFeatureId The feature in the package
+     *
+     * @return The subscription info.
+     */
+    @Override
+    @Nullable
+    public SubscriptionInfo getActiveSubscriptionInfoForIccId(@NonNull String iccId,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * Get the active {@link SubscriptionInfo} associated with the logical SIM slot index.
+     *
+     * @param slotIndex the logical SIM slot index which the subscription is inserted
+     * @param callingPackage The package making the call
+     * @param callingFeatureId The feature in the package
+     *
+     * @return SubscriptionInfo, null for Remote-SIMs or non-active logical SIM slot index.
+     */
+    @Override
+    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted
+     * by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}.
+     *
+     * @param callingPackage The package making the call
+     * @param callingFeatureId The feature in the package
+     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the
+     * device.
+     * <ul>
+     * <li>
+     * If null is returned the current state is unknown but if a
+     * {@link OnSubscriptionsChangedListener} has been registered
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future.
+     * </li>
+     * <li>
+     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+     * </li>
+     * <li>
+     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+     * then by {@link SubscriptionInfo#getSubscriptionId}.
+     * </li>
+     * </ul>
+     */
+    @Override
+    public List<SubscriptionInfo> getActiveSubscriptionInfoList(@NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * Get the number of active {@link SubscriptionInfo}.
+     *
+     * @param callingPackage The package making the call
+     * @param callingFeatureId The feature in the package
+     * @return the number of active subscriptions
+     */
+    @Override
+    public int getActiveSubInfoCount(@NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return 0;
+    }
+
+    /**
+     * @return the maximum number of subscriptions this device will support at any one time.
+     */
+    @Override
+    public int getActiveSubInfoCountMax() {
+        return 0;
+    }
+
+    /**
+     * @see SubscriptionManager#getAvailableSubscriptionInfoList
+     */
+    @Override
+    public List<SubscriptionInfo> getAvailableSubscriptionInfoList(@NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    /**
+     * @see SubscriptionManager#getAccessibleSubscriptionInfoList
+     */
+    @Override
+    public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(
+            @NonNull String callingPackage) {
+        return null;
+    }
+
+    /**
+     * @see SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
+     */
+    @Override
+    public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+
+    }
+
+    /**
+     * Add a new subscription record to subscription database if needed.
+     *
+     * @param iccId the IccId of the SIM card
+     * @param slotIndex the slot which the SIM is inserted
+     *
+     * @return 0 if success, negative if failed.
+     */
+    @Override
+    public int addSubInfoRecord(@NonNull String iccId, int slotIndex) {
+        return 0;
+    }
+
+    /**
+     * Add a new subscription info record, if needed.
+     *
+     * @param uniqueId This is the unique identifier for the subscription within the specific
+     * subscription type
+     * @param displayName human-readable name of the device the subscription corresponds to
+     * @param slotIndex the slot assigned to this device
+     * @param subscriptionType the type of subscription to be added
+     *
+     * @return 0 if success, < 0 on error
+     */
+    @Override
+    public int addSubInfo(@NonNull String uniqueId, @NonNull String displayName, int slotIndex,
+            @SubscriptionType int subscriptionType) {
+        return 0;
+    }
+
+    /**
+     * Remove subscription info record for the given device.
+     *
+     * @param uniqueId This is the unique identifier for the subscription within the specific
+     * subscription type.
+     * @param subscriptionType the type of subscription to be removed
+     *
+     * @return 0 if success, < 0 on error
+     */
+    @Override
+    public int removeSubInfo(@NonNull String uniqueId, int subscriptionType) {
+        return 0;
+    }
+
+    /**
+     * Set SIM icon tint color by simInfo index.
+     *
+     * @param tint the icon tint color of the SIM
+     * @param subId the unique subscription index in database
+     *
+     * @return the number of records updated
+     */
+    @Override
+    public int setIconTint(int tint, int subId) {
+        return 0;
+    }
+
+    /**
+     * Set display name by simInfo index with name source.
+     *
+     * @param displayName the display name of SIM card
+     * @param subId the unique SubscriptionInfo index in database
+     * @param nameSource 0: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT
+     *
+     * @return the number of records updated
+     */
+    @Override
+    public int setDisplayNameUsingSrc(@NonNull String displayName, int subId, int nameSource) {
+        return 0;
+    }
+
+    /**
+     * Set phone number by subscription id.
+     *
+     * @param number the phone number of the SIM
+     * @param subId the unique SubscriptionInfo index in database
+     *
+     * @return the number of records updated
+     */
+    @Override
+    public int setDisplayNumber(@NonNull String number, int subId) {
+        return 0;
+    }
+
+    /**
+     * Set data roaming by simInfo index
+     *
+     * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
+     * @param subId the unique SubscriptionInfo index in database
+     *
+     * @return the number of records updated
+     */
+    @Override
+    public int setDataRoaming(int roaming, int subId) {
+        return 0;
+    }
+
+    /**
+     * Switch to a certain subscription.
+     *
+     * @param opportunistic whether it’s opportunistic subscription
+     * @param subId the unique SubscriptionInfo index in database
+     * @param callingPackage The package making the call
+     *
+     * @return the number of records updated
+     */
+    @Override
+    public int setOpportunistic(boolean opportunistic, int subId, @NonNull String callingPackage) {
+        return 0;
+    }
+
+    /**
+     * Inform SubscriptionManager that subscriptions in the list are bundled as a group. Typically
+     * it's a primary subscription and an opportunistic subscription. It should only affect
+     * multi-SIM scenarios where primary and opportunistic subscriptions can be activated together.
+     *
+     * Being in the same group means they might be activated or deactivated together, some of them
+     * may be invisible to the users, etc.
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} permission or
+     * can manage all subscriptions in the list, according to their access rules.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @param callingPackage The package making the call
+     *
+     * @return groupUUID a UUID assigned to the subscription group. It returns null if fails.
+     */
+    @Override
+    public ParcelUuid createSubscriptionGroup(int[] subIdList, @NonNull String callingPackage) {
+        return null;
+    }
+
+    /**
+     * Set which subscription is preferred for cellular data. It's designed to overwrite default
+     * data subscription temporarily.
+     *
+     * @param subId which subscription is preferred to for cellular data
+     * @param needValidation whether validation is needed before switching
+     * @param callback callback upon request completion
+     */
+    @Override
+    public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
+            @Nullable ISetOpportunisticDataCallback callback) {
+    }
+
+    /**
+     * @return The subscription id of preferred subscription for cellular data. This reflects
+     * the active modem which can serve large amount of cellular data.
+     */
+    @Override
+    public int getPreferredDataSubscriptionId() {
+        return 0;
+    }
+
+    /**
+     * @return The list of opportunistic subscription info that can be accessed by the callers.
+     */
+    @Override
+    @NonNull
+    public List<SubscriptionInfo> getOpportunisticSubscriptions(@NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void removeSubscriptionsFromGroup(int[] subIdList, @NonNull ParcelUuid groupUuid,
+            @NonNull String callingPackage) {
+    }
+
+    @Override
+    public void addSubscriptionsIntoGroup(int[] subIdList, @NonNull ParcelUuid groupUuid,
+            @NonNull String callingPackage) {
+    }
+
+    @Override
+    public List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    @Override
+    public int getSlotIndex(int subId) {
+        return 0;
+    }
+
+    @Override
+    public int[] getSubId(int slotIndex) {
+        return null;
+    }
+
+    @Override
+    public int getDefaultSubId() {
+        return 0;
+    }
+
+    @Override
+    public int clearSubInfo() {
+        return 0;
+    }
+
+    @Override
+    public int getPhoneId(int subId) {
+        return 0;
+    }
+
+    /**
+     * @return Subscription id of the default cellular data. This reflects the user's default data
+     * choice, which might be a little bit different than the active one returned by
+     * {@link #getPreferredDataSubscriptionId()}.
+     */
+    @Override
+    public int getDefaultDataSubId() {
+        return 0;
+    }
+
+    @Override
+    public void setDefaultDataSubId(int subId) {
+    }
+
+    @Override
+    public int getDefaultVoiceSubId() {
+        return 0;
+    }
+
+    @Override
+    public void setDefaultVoiceSubId(int subId) {
+    }
+
+    @Override
+    public int getDefaultSmsSubId() {
+        return 0;
+    }
+
+    @Override
+    public void setDefaultSmsSubId(int subId) {
+    }
+
+    @Override
+    public int[] getActiveSubIdList(boolean visibleOnly) {
+        return null;
+    }
+
+    @Override
+    public int setSubscriptionProperty(int subId, @NonNull String propKey,
+            @NonNull String propValue) {
+        return 0;
+    }
+
+    @Override
+    public String getSubscriptionProperty(int subId, @NonNull String propKey,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    @Override
+    public boolean setSubscriptionEnabled(boolean enable, int subId) {
+        return true;
+    }
+
+    @Override
+    public boolean isSubscriptionEnabled(int subId) {
+        return true;
+    }
+
+    @Override
+    public int getEnabledSubscriptionId(int slotIndex) {
+        return 0;
+    }
+
+    @Override
+    public int getSimStateForSlotIndex(int slotIndex) {
+        return 0;
+    }
+
+    @Override
+    public boolean isActiveSubId(int subId, @NonNull String callingPackage,
+            @NonNull String callingFeatureId) {
+        return true;
+    }
+
+    @Override
+    public int getActiveDataSubscriptionId() {
+        return 0;
+    }
+
+    @Override
+    public boolean canDisablePhysicalSubscription() {
+        return false;
+    }
+
+    @Override
+    public int setUiccApplicationsEnabled(boolean enabled, int subscriptionId) {
+        return 0;
+    }
+
+    @Override
+    public int setDeviceToDeviceStatusSharing(int sharing, int subId) {
+        return 0;
+    }
+
+    @Override
+    public int setDeviceToDeviceStatusSharingContacts(@NonNull String contacts,
+            int subscriptionId) {
+        return 0;
+    }
+
+    @Override
+    public String getPhoneNumber(int subId, int source,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    @Override
+    public String getPhoneNumberFromFirstAvailableSource(int subId,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+        return null;
+    }
+
+    @Override
+    public void setPhoneNumber(int subId, int source, @NonNull String number,
+            @NonNull String callingPackage, @NonNull String callingFeatureId) {
+    }
+
+    /**
+     * Set the Usage Setting for this subscription.
+     *
+     * @param usageSetting the usage setting for this subscription
+     * @param subId the unique SubscriptionInfo index in database
+     * @param callingPackage The package making the IPC.
+     *
+     * @throws SecurityException if doesn't have MODIFY_PHONE_STATE or Carrier Privileges
+     */
+    @Override
+    public int setUsageSetting(int usageSetting, int subId, @NonNull String callingPackage) {
+        return 0;
+    }
+
+    /**
+     * Set UserHandle for this subscription
+     *
+     * @param userHandle the userHandle associated with the subscription
+     * Pass {@code null} user handle to clear the association
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated.
+     *
+     * @throws SecurityException if doesn't have required permission.
+     * @throws IllegalArgumentException if subId is invalid.
+     */
+    @Override
+    public int setSubscriptionUserHandle(@Nullable UserHandle userHandle, int subId) {
+        return 0;
+    }
+
+    /**
+     * Get UserHandle of this subscription.
+     *
+     * @param subId the unique SubscriptionInfo index in database
+     * @return userHandle associated with this subscription
+     * or {@code null} if subscription is not associated with any user.
+     *
+     * @throws SecurityException if doesn't have required permission.
+     * @throws IllegalArgumentException if subId is invalid.
+     */
+    @Override
+    public UserHandle getSubscriptionUserHandle(int subId) {
+        return null;
+    }
+
+    /**
+     * Log debug messages.
+     *
+     * @param s debug messages
+     */
+    private void log(@NonNull String s) {
+        Rlog.d(LOG_TAG, s);
+    }
+
+    /**
+     * Log error messages.
+     *
+     * @param s error messages
+     */
+    private void loge(@NonNull String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+
+    /**
+     * Log verbose messages.
+     *
+     * @param s debug messages.
+     */
+    private void logv(@NonNull String s) {
+        if (VDBG) Rlog.v(LOG_TAG, s);
+    }
+
+    /**
+     * Log debug messages and also log into the local log.
+     *
+     * @param s debug messages
+     */
+    private void logl(@NonNull String s) {
+        log(s);
+        mLocalLog.log(s);
+    }
+
+    /**
+     * Dump the state of {@link SubscriptionManagerService}.
+     *
+     * @param fd File descriptor
+     * @param printWriter Print writer
+     * @param args Arguments
+     */
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter printWriter,
+            @NonNull String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println(SubscriptionManagerService.class.getSimpleName() + ":");
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
index 21a6e37..90c9491 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
@@ -23,10 +23,12 @@
 import android.os.Message;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.gsm.UsimPhoneBookManager;
 
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.Locale;
 
 /**
  * {@hide}
@@ -58,14 +60,16 @@
     static final int EVENT_UPDATE_ADN_DONE = 2;
 
     //***** Constructor
-
-
-
     AdnRecordCache(IccFileHandler fh) {
         mFh = fh;
         mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, this);
     }
 
+    public AdnRecordCache(IccFileHandler fh, UsimPhoneBookManager usimPhoneBookManager) {
+        mFh = fh;
+        mUsimPhoneBookManager = usimPhoneBookManager;
+    }
+
     //***** Called from SIMRecords
 
     /**
@@ -156,14 +160,14 @@
         int extensionEF = extensionEfForEf(efid);
         if (extensionEF < 0) {
             sendErrorResponse(response, "EF is not known ADN-like EF:0x" +
-                    Integer.toHexString(efid).toUpperCase());
+                    Integer.toHexString(efid).toUpperCase(Locale.ROOT));
             return;
         }
 
         Message pendingResponse = mUserWriteResponse.get(efid);
         if (pendingResponse != null) {
             sendErrorResponse(response, "Have pending update for EF:0x" +
-                    Integer.toHexString(efid).toUpperCase());
+                    Integer.toHexString(efid).toUpperCase(Locale.ROOT));
             return;
         }
 
@@ -190,16 +194,14 @@
      */
     public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
             String pin2, Message response) {
-
         int extensionEF;
         extensionEF = extensionEfForEf(efid);
 
         if (extensionEF < 0) {
             sendErrorResponse(response, "EF is not known ADN-like EF:0x" +
-                    Integer.toHexString(efid).toUpperCase());
+                    Integer.toHexString(efid).toUpperCase(Locale.ROOT));
             return;
         }
-
         ArrayList<AdnRecord>  oldAdnList;
 
         if (efid == EF_PBR) {
@@ -207,13 +209,11 @@
         } else {
             oldAdnList = getRecordsIfLoaded(efid);
         }
-
         if (oldAdnList == null) {
             sendErrorResponse(response, "Adn list not exist for EF:0x" +
-                    Integer.toHexString(efid).toUpperCase());
+                    Integer.toHexString(efid).toUpperCase(Locale.ROOT));
             return;
         }
-
         int index = -1;
         int count = 1;
         for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
@@ -223,7 +223,6 @@
             }
             count++;
         }
-
         if (index == -1) {
             sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
             return;
@@ -244,7 +243,7 @@
 
         if (pendingResponse != null) {
             sendErrorResponse(response, "Have pending update for EF:0x" +
-                    Integer.toHexString(efid).toUpperCase());
+                    Integer.toHexString(efid).toUpperCase(Locale.ROOT));
             return;
         }
 
@@ -307,7 +306,7 @@
             if (response != null) {
                 AsyncResult.forMessage(response).exception
                     = new RuntimeException("EF is not known ADN-like EF:0x" +
-                        Integer.toHexString(efid).toUpperCase());
+                        Integer.toHexString(efid).toUpperCase(Locale.ROOT));
                 response.sendToTarget();
             }
 
@@ -342,7 +341,6 @@
     handleMessage(Message msg) {
         AsyncResult ar;
         int efid;
-
         switch(msg.what) {
             case EVENT_LOAD_ALL_ADN_LIKE_DONE:
                 /* arg1 is efid, obj.result is ArrayList<AdnRecord>*/
@@ -381,4 +379,24 @@
                 break;
         }
     }
+
+    @VisibleForTesting
+    protected void setAdnLikeWriters(int key, ArrayList<Message> waiters) {
+        mAdnLikeWaiters.put(EF_MBDN, waiters);
+    }
+
+    @VisibleForTesting
+    protected void setAdnLikeFiles(int key, ArrayList<AdnRecord> adnRecordList) {
+        mAdnLikeFiles.put(EF_MBDN, adnRecordList);
+    }
+
+    @VisibleForTesting
+    protected void setUserWriteResponse(int key, Message message) {
+        mUserWriteResponse.put(EF_MBDN, message);
+    }
+
+    @VisibleForTesting
+    protected UsimPhoneBookManager getUsimPhoneBookManager() {
+        return mUsimPhoneBookManager;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
index a688c6e..5b0a6a3 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
@@ -59,6 +59,7 @@
     static final int EVENT_EF_LINEAR_RECORD_SIZE_DONE = 4;
     static final int EVENT_UPDATE_RECORD_DONE = 5;
 
+    static final int VOICEMAIL_ALPHATAG_ARG = 1;
     //***** Constructor
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -177,14 +178,34 @@
                     data = adn.buildAdnString(recordSize[0]);
 
                     if(data == null) {
-                        throw new RuntimeException("wrong ADN format",
-                                ar.exception);
+                        /**
+                         * The voicemail number saving to the SIM is in name(alphaTag) and number
+                         * format. {@link recordSize[0]} indicates the SIM EF memory size that the
+                         * sim can have to save both voicemail name and number. 14 byte of memory
+                         * is reserved to save the voicemail number and remaining memory is reserved
+                         * for the alphaTag. In case if we receive the alphaTag which is more than
+                         * the reserved memory size then SIM will throw the exception and it don't
+                         * save both the voicemail number and alphaTag. To avoid this problem, in
+                         * case alphaTag length is more we nullify the alphaTag and save the same.
+                         */
+                        if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+                            adn.mAlphaTag = null;
+                            data = adn.buildAdnString(recordSize[0]);
+                        }
+                        if (data == null) {
+                            throw new RuntimeException("wrong ADN format",
+                                    ar.exception);
+                        }
                     }
 
-
-                    mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
-                            data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
-
+                    // Send adn record to caller to track the changes made to alphaTag
+                    if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+                        mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
+                                data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE, adn));
+                    } else {
+                        mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
+                                data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
+                    }
                     mPendingExtLoads = 1;
 
                     break;
@@ -195,7 +216,12 @@
                                 ar.exception);
                     }
                     mPendingExtLoads = 0;
-                    mResult = null;
+                    // send the adn record back to caller through the result of AsyncResult
+                    if (mUserResponse.arg1 == VOICEMAIL_ALPHATAG_ARG) {
+                        mResult =  ar.userObj;
+                    } else {
+                        mResult = null;
+                    }
                     break;
                 case EVENT_ADN_LOAD_DONE:
                     ar = (AsyncResult)(msg.obj);
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 5eae070..a9275be 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -45,6 +45,8 @@
     static final int EF_SST = 0x6F38;
     static final int EF_CFIS = 0x6FCB;
     static final int EF_IMG = 0x4F20;
+    static final int EF_PSISMSC = 0x6FE5;
+    static final int EF_SMSS = 0x6F43;
 
     // USIM SIM file ids from TS 131.102
     public static final int EF_PBR = 0x4F30;
@@ -84,7 +86,6 @@
     static final int EF_DOMAIN = 0x6F03;
     static final int EF_IST = 0x6F07;
     static final int EF_PCSCF = 0x6F09;
-    static final int EF_PSI = 0x6FE5;
 
     //PLMN Selection Information w/ Access Technology TS 131.102
     static final int EF_PLMN_W_ACT = 0x6F60;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index f3b1d0b..5f8ccb4 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.Message;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
 
 import java.util.ArrayList;
@@ -108,7 +109,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected final String mAid;
 
-    static class LoadLinearFixedContext {
+    public static class LoadLinearFixedContext {
 
         int mEfid;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -145,14 +146,11 @@
             mOnLoaded = onLoaded;
             mPath = path;
         }
+    }
 
-        LoadLinearFixedContext(int efid, Message onLoaded) {
-            mEfid = efid;
-            mRecordNum = 1;
-            mLoadAll = true;
-            mOnLoaded = onLoaded;
-            mPath = null;
-        }
+    @VisibleForTesting
+    public int getEfid(LoadLinearFixedContext lc) {
+        return lc.mEfid;
     }
 
     /**
@@ -164,6 +162,13 @@
         mCi = ci;
     }
 
+    @VisibleForTesting
+    public IccFileHandler(CommandsInterface ci) {
+        mParentApp = null;
+        mAid = null;
+        mCi = ci;
+    }
+
     public void dispose() {
     }
 
@@ -267,8 +272,7 @@
      * @param path Path of the EF on the card
      * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the size of data int
      */
-    public void getEFTransparentRecordSize(int fileid, String path, Message onLoaded) {
-        String efPath = (path == null) ? getEFPath(fileid) : path;
+    public void getEFTransparentRecordSize(int fileid, Message onLoaded) {
         Message response = obtainMessage(EVENT_GET_EF_TRANSPARENT_SIZE_DONE, fileid, 0, onLoaded);
         mCi.iccIOForApp(
                 COMMAND_GET_RESPONSE,
@@ -284,16 +288,6 @@
     }
 
     /**
-     * Get record size for a transparent EF
-     *
-     * @param fileid EF id
-     * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the size of the data int
-     */
-    public void getEFTransparentRecordSize(int fileid, Message onLoaded) {
-        getEFTransparentRecordSize(fileid, getEFPath(fileid), onLoaded);
-    }
-
-    /**
      * Load all records from a SIM Linear Fixed EF
      *
      * @param fileid EF id
@@ -693,7 +687,7 @@
         case EF_EXT1:
         case EF_EXT2:
         case EF_EXT3:
-        case EF_PSI:
+        case EF_PSISMSC:
             return MF_SIM + DF_TELECOM;
 
         case EF_ICCID:
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 3a7db7f..da112b1 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -30,19 +30,23 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.gsm.SimTlv;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Objects;
@@ -53,8 +57,11 @@
  * {@hide}
  */
 public abstract class IccRecords extends Handler implements IccConstants {
+    private static final String LOG_TAG = "IccRecords";
     protected static final boolean DBG = true;
-    protected static final boolean VDBG = false; // STOPSHIP if true
+    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+    protected static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     public static final int PLMN_MIN_LENGTH = CellIdentity.MCC_LENGTH
             + CellIdentity.MNC_MIN_LENGTH;
@@ -119,6 +126,10 @@
 
     protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
     protected int mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
+    // EF_SMSS fields tpmr invalid, min and max declarations
+    protected static final int SMSS_INVALID_TPMR = -1;
+    private static final int TPMR_MIN = 0x00;
+    private static final int TPMR_MAX = 0xFF;
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
     public String mIccId;  // Includes only decimals (no hex)
@@ -173,6 +184,14 @@
     protected String[] mEhplmns;
     protected String[] mFplmns;
 
+    // SIP or TEL URI [ Public Service Identity of the SM-SC]
+    // Reference: TS 31.102 section 4.5.9
+    protected String mPsiSmsc;
+
+    // EF_SMSS value which is combination of TPMR and Memory exceed flag
+    // Reference: TS 31.102 section 4.2.9
+    protected byte[] mSmssValues;
+
     CarrierTestOverride mCarrierTestOverride;
 
     //Arbitrary offset for the Handler
@@ -232,6 +251,12 @@
     // arrive and returning null to the callers.
     private static final long ICC_SIM_CHALLENGE_TIMEOUT_MILLIS = 2500;
 
+    // TAG value to retrieve EF_PSISMSC from parsed SimTlv object
+    private static final int TAG_TLV_USIM_VALUE_80 = 0x80;
+
+    // call back received on this upon EF_SMSS record update.
+    public static final int EVENT_SET_SMSS_RECORD_DONE = 201;
+
     /**
      * There are two purposes for this class. First, each instance of AuthAsyncResponse acts as a
      * lock to for calling thead to wait in getIccSimChallengeResponse(). Second, pass the IMS
@@ -954,6 +979,26 @@
                 }
                 break;
 
+            case EVENT_SET_SMSS_RECORD_DONE:
+                ar = (AsyncResult) msg.obj;
+                SmssRecord smssRecord = null;
+                if (ar.userObj != null) {
+                    smssRecord = (SmssRecord) ar.userObj;
+                }
+                if (ar.exception == null && smssRecord.getSmssValue() != null) {
+                    mSmssValues = smssRecord.getSmssValue().clone();
+                } else {
+                    loge("SIM EF_SMSS field updating error=" + ar.exception);
+                }
+                if (smssRecord != null && smssRecord.getMessage() != null) {
+                    Message message = smssRecord.getMessage();
+                    AsyncResult.forMessage(message, ar.result, ar.exception);
+                    message.sendToTarget();
+                } else {
+                    loge("smssRecord or smssRecord.getMessage() object is null");
+                }
+                break;
+
             default:
                 super.handleMessage(msg);
         }
@@ -1306,6 +1351,34 @@
         return mSmsCountOnIcc;
     }
 
+    /**
+     * parse EF PSISMSC value [3GPP TS 31.102 Section 4.5.9]
+     *
+     * @param data read from EF PSISMSC field of type byte[]
+     * @return SIP URI or tel URI of type string
+     */
+    protected String parseEfPsiSmsc(byte[] data) {
+        SimTlv tlv = new SimTlv(data, 0, data.length);
+        if (tlv.isValidObject() && tlv.getData() != null) {
+            if (tlv.getTag() == TAG_TLV_USIM_VALUE_80) {
+                return new String(tlv.getData(), Charset.forName("UTF-8"));
+            }
+        }
+        if (VDBG) {
+            log("Can't find EF PSISMSC field in SIM = " + IccUtils.bytesToHexString(data));
+        }
+        return null;
+    }
+
+    /**
+     * SMSC address read from the elementary file EF_PSISMSC
+     *
+     * @return SIP URI or tel URI of type string
+     */
+    public String getSmscIdentity() {
+        return mPsiSmsc;
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("IccRecords: " + this);
         pw.println(" mDestroyed=" + mDestroyed);
@@ -1532,4 +1605,73 @@
             return "{fullName = " + fullName + ", shortName = " + shortName + "}";
         }
     }
+
+    /**
+     * Sets the elementary (EF_SMSS) field with latest last used TP-Message reference value.
+     * First byte of EF_SMSS represents the TPMR value as per the spec
+     * (Section 4.2.9 of 3GPP TS 31.102)
+     *
+     * @param tpmr: Last used TP-Message reference parameter of type int
+     * @param onComplete: android.os.Message to be notified upon completion
+     */
+    public void setSmssTpmrValue(int tpmr, Message onComplete) {
+        if(VDBG) log("setSmssTpmrValue()");
+        if (mSmssValues != null && mSmssValues.length > 0 && tpmr >= TPMR_MIN && tpmr <= TPMR_MAX) {
+            byte[] tempSmss = mSmssValues.clone();
+            tempSmss[0] = (byte) (tpmr & 0xFF);
+            SmssRecord smssRecord = createSmssRecord(onComplete, tempSmss);
+            mFh.updateEFTransparent(IccConstants.EF_SMSS, tempSmss,
+                    obtainMessage(EVENT_SET_SMSS_RECORD_DONE, smssRecord));
+        } else if (onComplete != null) {
+            loge("Failed to set EF_SMSS [TPMR] field to SIM");
+            if (mSmssValues == null || mSmssValues.length <= 0) {
+                AsyncResult.forMessage((onComplete)).exception =
+                        new FileNotFoundException("EF_SMSS file not found");
+            } else if (tpmr < TPMR_MIN || tpmr > TPMR_MAX) {
+                AsyncResult.forMessage((onComplete)).exception =
+                        new IllegalArgumentException("TPMR value is not in allowed range");
+            }
+            onComplete.sendToTarget();
+        }
+    }
+
+    /**
+     * Fetches the last used TPMR value from elementary (EF_SMSS) field. First byte of EF_SMSS
+     * represents the TPMR value as per the spec (Section 4.2.9 of 3GPP TS 31.102)
+     *
+     * @return TP-Message reference parameter of type int, -1 in case if it fails to read the
+     * EF_SMSS field from the sim.
+     */
+    public int getSmssTpmrValue() {
+        if (mSmssValues != null && mSmssValues.length > 0) {
+            return  (mSmssValues[0] & 0xFF);
+        }
+        loge("IccRecords - EF_SMSS is null");
+        return SMSS_INVALID_TPMR;
+    }
+
+    @VisibleForTesting
+    public SmssRecord createSmssRecord(Message msg, byte[] smss) {
+        return new SmssRecord(msg, smss);
+    }
+
+
+    static class SmssRecord {
+
+        private Message mMsg;
+        private byte[] mSmss;
+
+        SmssRecord (Message msg, byte[] smss) {
+            mMsg = msg;
+            mSmss = smss;
+        }
+
+        private byte[] getSmssValue() {
+            return mSmss;
+        }
+
+        private Message getMessage() {
+            return mMsg;
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java b/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
index 9a5e10d..7197fc8 100644
--- a/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
+++ b/src/java/com/android/internal/telephony/uicc/IccSimPortInfo.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.uicc;
 
-import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
 
 import java.util.Objects;
@@ -50,9 +49,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("{").append("iccid=")
-                .append(SubscriptionInfo.givePrintableIccid(mIccId)).append(",")
-                .append("logicalSlotIndex=").append(mLogicalSlotIndex).append(",")
+        sb.append("{").append("logicalSlotIndex=").append(mLogicalSlotIndex).append(",")
                 .append("portActive=").append(mPortActive)
                 .append("}");
         return sb.toString();
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
index 412295d..7666f4c 100644
--- a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
@@ -38,6 +38,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Utility methods for installing the carrier app when a SIM is insterted without the carrier app
@@ -178,7 +179,7 @@
      */
     @VisibleForTesting
     public static String getAppNameFromPackageName(String packageName, String mapString) {
-        packageName = packageName.toLowerCase();
+        packageName = packageName.toLowerCase(Locale.ROOT);
         final String pairDelim = "\\s*;\\s*";
         final String keyValueDelim = "\\s*:\\s*";
 
diff --git a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
index fe900cb..4d5f8d1 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimFileHandler.java
@@ -38,6 +38,7 @@
         case EF_DOMAIN:
         case EF_IST:
         case EF_PCSCF:
+        case EF_SMSS:
             return MF_SIM + DF_ADF;
         }
         String path = getCommonIccEFPath(efid);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java b/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java
new file mode 100644
index 0000000..289229c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/IsimServiceTable.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+public final class IsimServiceTable extends IccServiceTable {
+    private static final String TAG = "IsimServiceTable";
+    public enum IsimService {
+        PCSCF_ADDRESS,
+        GBA,                                // Generic Bootstrapping Architecture (GBA)
+        HTTP_DIGEST,
+        GBA_LOCALKEY_ESTABLISHMENT,         // GBA-based Local Key Establishment Mechanism
+        PCSCF_DISCOVERY_FOR_IMS,            // Support of P-CSCF discovery for IMS Local Break Out
+        SMS,
+        SMSR,                               // Short Message Status Reports
+        SM_OVERIP_AND_DATA_DL_VIA_SMS_PP,   // Support for SM-over-IP including data download via
+        // SMS-PP
+        COMMUNICATION_CONTROL_FOR_IMS_BY_ISIM,
+        UICC_ACCESS_TO_IMS
+    }
+
+    public IsimServiceTable(byte[] table) {
+        super(table);
+    }
+
+    public boolean isAvailable(IsimService service) {
+        return super.isAvailable(service.ordinal());
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected Object[] getValues() {
+        return IsimService.values();
+    }
+
+    public byte[] getISIMServiceTable() {
+        return mServiceTable;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 12284af..9591a498 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -23,7 +23,9 @@
 import android.os.Build;
 import android.os.Message;
 import android.telephony.SubscriptionManager;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.gsm.SimTlv;
 import com.android.telephony.Rlog;
@@ -41,7 +43,9 @@
     protected static final String LOG_TAG = "IsimUiccRecords";
 
     private static final boolean DBG = true;
-    private static final boolean VDBG = false; // STOPSHIP if true
+    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+    private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
     private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
                                                         // STOPSHIP if true
     public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
@@ -69,7 +73,9 @@
                 + " mIsimDomain=" + mIsimDomain
                 + " mIsimImpu=" + Arrays.toString(mIsimImpu)
                 + " mIsimIst=" + mIsimIst
-                + " mIsimPcscf=" + Arrays.toString(mIsimPcscf)) : "");
+                + " mIsimPcscf=" + Arrays.toString(mIsimPcscf)
+                + " mPsiSmsc=" + mPsiSmsc
+                + " mSmss TPMR=" + getSmssTpmrValue()) : "");
     }
 
     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -111,7 +117,6 @@
                     broadcastRefresh();
                     super.handleMessage(msg);
                     break;
-
                 default:
                     super.handleMessage(msg);   // IccRecords handles generic record load responses
 
@@ -143,6 +148,13 @@
         mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
         mRecordsToLoad++;
+        mFh.loadEFTransparent(EF_SMSS,  obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimSmssLoaded()));
+        mRecordsToLoad++;
+
+        mFh.loadEFLinearFixed(EF_PSISMSC, 1, obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPsiSmscLoaded()));
+        mRecordsToLoad++;
 
         if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
     }
@@ -212,6 +224,28 @@
             if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
         }
     }
+
+    @VisibleForTesting
+    public EfIsimIstLoaded getIsimIstObject() {
+        return new EfIsimIstLoaded();
+    }
+
+    private class EfIsimSmssLoaded implements IccRecords.IccRecordLoaded {
+
+        @Override
+        public String getEfName() {
+            return "EF_ISIM_SMSS";
+        }
+
+        @Override
+        public void onRecordLoaded(AsyncResult ar) {
+            mSmssValues = (byte[]) ar.result;
+            if (VDBG) {
+                log("IsimUiccRecords - EF_SMSS TPMR value = " + getSmssTpmrValue());
+            }
+        }
+    }
+
     private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
         public String getEfName() {
             return "EF_ISIM_PCSCF";
@@ -229,6 +263,29 @@
         }
     }
 
+    private class EfIsimPsiSmscLoaded implements IccRecords.IccRecordLoaded {
+
+        @Override
+        public String getEfName() {
+            return "EF_ISIM_PSISMSC";
+        }
+
+        @Override
+        public void onRecordLoaded(AsyncResult ar) {
+            byte[] data = (byte[]) ar.result;
+            if (data != null && data.length > 0) {
+                mPsiSmsc = parseEfPsiSmsc(data);
+                if (VDBG) {
+                    log("IsimUiccRecords - EF_PSISMSC value = " + mPsiSmsc);
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public EfIsimPsiSmscLoaded getPsiSmscObject() {
+        return new EfIsimPsiSmscLoaded();
+    }
     /**
      * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
      * with tag value 0x80.
@@ -432,19 +489,28 @@
         pw.println("IsimRecords: " + this);
         pw.println(" extends:");
         super.dump(fd, pw, args);
+        pw.println(" mIsimServiceTable=" + getIsimServiceTable());
         if (DUMP_RECORDS) {
             pw.println(" mIsimImpi=" + mIsimImpi);
             pw.println(" mIsimDomain=" + mIsimDomain);
             pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
-            pw.println(" mIsimIst" + mIsimIst);
             pw.println(" mIsimPcscf" + Arrays.toString(mIsimPcscf));
+            pw.println(" mPsismsc=" + mPsiSmsc);
+            pw.println(" mSmss TPMR=" + getSmssTpmrValue());
         }
         pw.flush();
     }
 
+    // Just to return the Enums of service table to print in DUMP
+    private IsimServiceTable getIsimServiceTable() {
+        if (mIsimIst != null) {
+            byte[] istTable = IccUtils.hexStringToBytes(mIsimIst);
+            return new IsimServiceTable(istTable);
+        }
+        return null;
+    }
     @Override
     public int getVoiceMessageCount() {
         return 0; // Not applicable to Isim
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index b6032ec..f94bcfb 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -31,8 +31,10 @@
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.SmsConstants;
@@ -54,8 +56,9 @@
     protected static final String LOG_TAG = "SIMRecords";
 
     private static final boolean CRASH_RIL = false;
-
-    private static final boolean VDBG = false;
+    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
+    private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     // ***** Instance Variables
 
@@ -108,7 +111,9 @@
                 + " efCPHS_MWI=" + IccUtils.bytesToHexString(mEfCPHS_MWI)
                 + " mEfCff=" + IccUtils.bytesToHexString(mEfCff)
                 + " mEfCfis=" + IccUtils.bytesToHexString(mEfCfis)
-                + " getOperatorNumeric=" + getOperatorNumeric();
+                + " getOperatorNumeric=" + getOperatorNumeric()
+                + " mPsiSmsc=" + mPsiSmsc
+                + " TPMR=" + getSmssTpmrValue();
     }
 
     // ***** Constants
@@ -163,7 +168,7 @@
     private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE;
     protected static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE;
     protected static final int EVENT_GET_OPL_DONE = 16 + SIM_RECORD_EVENT_BASE;
-    private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
+    protected static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
     private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE;
     private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE;
     private static final int EVENT_SET_MBDN_DONE = 20 + SIM_RECORD_EVENT_BASE;
@@ -184,6 +189,9 @@
     private static final int EVENT_GET_FPLMN_DONE = 41 + SIM_RECORD_EVENT_BASE;
     private static final int EVENT_GET_FPLMN_SIZE_DONE = 42 + SIM_RECORD_EVENT_BASE;
     private static final int EVENT_SET_FPLMN_DONE = 43 + SIM_RECORD_EVENT_BASE;
+    protected static final int EVENT_GET_SMSS_RECORD_DONE = 46 + SIM_RECORD_EVENT_BASE;
+    protected static final int EVENT_GET_PSISMSC_DONE = 47 + SIM_RECORD_EVENT_BASE;
+
     // ***** Constructor
 
     public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -272,6 +280,18 @@
         return mUsimServiceTable;
     }
 
+    /**
+     * Fetches the USIM service table from UsimServiceTable
+     *
+     * @return HexString representation of USIM service table
+     */
+    public String getSimServiceTable() {
+        if (mUsimServiceTable != null) {
+            return IccUtils.bytesToHexString(mUsimServiceTable.getUSIMServiceTable());
+        }
+        return null;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int getExtFromEf(int ef) {
         int ext;
@@ -378,18 +398,20 @@
         mNewVoiceMailTag = alphaTag;
 
         AdnRecord adn = new AdnRecord(mNewVoiceMailTag, mNewVoiceMailNum);
-
         if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
 
             new AdnRecordLoader(mFh).updateEF(adn, EF_MBDN, EF_EXT6,
                     mMailboxIndex, null,
-                    obtainMessage(EVENT_SET_MBDN_DONE, onComplete));
+                    obtainMessage(EVENT_SET_MBDN_DONE, AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+                            0 /* ignored arg2 */, onComplete));
 
         } else if (isCphsMailboxEnabled()) {
 
             new AdnRecordLoader(mFh).updateEF(adn, EF_MAILBOX_CPHS,
                     EF_EXT1, 1, null,
-                    obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));
+                    obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
+                            AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+                            0 /* ignored arg2 */, onComplete));
 
         } else {
             AsyncResult.forMessage((onComplete)).exception =
@@ -639,7 +661,6 @@
                     " while being destroyed. Ignoring.");
             return;
         }
-
         try {
             switch (msg.what) {
                 /* IO events */
@@ -1013,10 +1034,20 @@
 
                     if (DBG) log("EVENT_SET_MBDN_DONE ex:" + ar.exception);
                     if (ar.exception == null) {
+                        /**
+                         * Check for any changes made to voicemail alphaTag while saving to SIM.
+                         * if voicemail alphaTag length is more than allowed limit of SIM EF then
+                         * null alphaTag will be saved to SIM {@code AdnRecordLoader}.
+                         */
+                        if (ar.result != null) {
+                            AdnRecord adnRecord = (AdnRecord) (ar.result);
+                            if (adnRecord != null) {
+                                mNewVoiceMailTag = adnRecord.mAlphaTag;
+                            }
+                        }
                         mVoiceMailNum = mNewVoiceMailNum;
                         mVoiceMailTag = mNewVoiceMailTag;
                     }
-
                     if (isCphsMailboxEnabled()) {
                         adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
                         Message onCphsCompleted = (Message) ar.userObj;
@@ -1040,7 +1071,8 @@
                         new AdnRecordLoader(mFh)
                                 .updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
                                 obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
-                                        onCphsCompleted));
+                                       AdnRecordLoader.VOICEMAIL_ALPHATAG_ARG,
+                                        0 /* ignored arg2 */, onCphsCompleted));
                     } else {
                         if (ar.userObj != null) {
                             CarrierConfigManager configManager = (CarrierConfigManager)
@@ -1072,6 +1104,12 @@
                     isRecordLoadResponse = false;
                     ar = (AsyncResult) msg.obj;
                     if (ar.exception == null) {
+                        if (ar.result != null) {
+                            AdnRecord adnRecord = (AdnRecord) (ar.result);
+                            if (adnRecord != null) {
+                                mNewVoiceMailTag = adnRecord.mAlphaTag;
+                            }
+                        }
                         mVoiceMailNum = mNewVoiceMailNum;
                         mVoiceMailTag = mNewVoiceMailTag;
                     } else {
@@ -1291,6 +1329,35 @@
                     }
                     break;
 
+                case EVENT_GET_PSISMSC_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        loge("Failed to read USIM EF_PSISMSC field error=" + ar.exception);
+                    } else {
+                        data = (byte[]) ar.result;
+                        if (data != null && data.length > 0) {
+                            mPsiSmsc = parseEfPsiSmsc(data);
+                            if (VDBG) {
+                                log("SIMRecords - EF_PSISMSC value = " + mPsiSmsc);
+                            }
+                        }
+                    }
+                    break;
+
+                case EVENT_GET_SMSS_RECORD_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        loge("Failed to read USIM EF_SMSS field error=" + ar.exception);
+                    } else {
+                        mSmssValues = (byte[]) ar.result;
+                        if (VDBG) {
+                            log("SIMRecords - EF_SMSS TPMR value = " + getSmssTpmrValue());
+                        }
+                    }
+                    break;
+
                 default:
                     super.handleMessage(msg);   // IccRecords handles generic record load responses
             }
@@ -1680,12 +1747,18 @@
         mFh.getEFLinearRecordSize(EF_SMS, obtainMessage(EVENT_GET_SMS_RECORD_SIZE_DONE));
         mRecordsToLoad++;
 
+        mFh.loadEFLinearFixed(EF_PSISMSC, 1, obtainMessage(EVENT_GET_PSISMSC_DONE));
+        mRecordsToLoad++;
+
         // XXX should seek instead of examining them all
         if (false) { // XXX
             mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
             mRecordsToLoad++;
         }
 
+        mFh.loadEFTransparent(EF_SMSS, obtainMessage(EVENT_GET_SMSS_RECORD_DONE));
+        mRecordsToLoad++;
+
         if (CRASH_RIL) {
             String sms = "0107912160130310f20404d0110041007030208054832b0120"
                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
@@ -2110,6 +2183,11 @@
         log("[CSP] Value Added Service Group (0xC0), not found!");
     }
 
+    @VisibleForTesting
+    public void setMailboxIndex(int mailboxIndex) {
+        mMailboxIndex = mailboxIndex;
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("SIMRecords: " + this);
@@ -2144,6 +2222,8 @@
         pw.println(" mHplmnActRecords[]=" + Arrays.toString(mHplmnActRecords));
         pw.println(" mFplmns[]=" + Arrays.toString(mFplmns));
         pw.println(" mEhplmns[]=" + Arrays.toString(mEhplmns));
+        pw.println(" mPsismsc=" + mPsiSmsc);
+        pw.println(" TPMR=" + getSmssTpmrValue());
         pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 689e4b7..54324b9 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -31,6 +31,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 
 /**
  * {@hide}
@@ -51,7 +52,7 @@
     protected String mCardId;
     protected boolean mIsSupportsMultipleEnabledProfiles;
 
-    protected HashMap<Integer, UiccPort> mUiccPorts = new HashMap<>();
+    protected LinkedHashMap<Integer, UiccPort> mUiccPorts = new LinkedHashMap<>();
     private HashMap<Integer, Integer> mPhoneIdToPortIdx = new HashMap<>();
 
     public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock,
@@ -213,7 +214,7 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("UiccCard:");
         pw.println(" mCardState=" + mCardState);
-        pw.println(" mCardId=" + mCardId);
+        pw.println(" mCardId=" + Rlog.pii(LOG_TAG, mCardId));
         pw.println(" mNumberOfPorts=" + mUiccPorts.size());
         pw.println( "mIsSupportsMultipleEnabledProfiles=" + mIsSupportsMultipleEnabledProfiles);
         pw.println();
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 3839610..9454ade 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -59,6 +59,9 @@
      */
     public static final int AUTH_CONTEXT_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
     public static final int AUTH_CONTEXT_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
+    public static final int AUTH_CONTEXT_GBA_BOOTSTRAP = PhoneConstants.AUTH_CONTEXT_GBA_BOOTSTRAP;
+    public static final int AUTHTYPE_GBA_NAF_KEY_EXTERNAL =
+            PhoneConstants.AUTHTYPE_GBA_NAF_KEY_EXTERNAL;
     public static final int AUTH_CONTEXT_UNDEFINED = PhoneConstants.AUTH_CONTEXT_UNDEFINED;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 70cfce0..4743bce 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -760,10 +760,10 @@
 
         IccCardStatus status = (IccCardStatus)ar.result;
 
-        logWithLocalLog("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
+        logWithLocalLog("onGetIccCardStatusDone: phoneId-" + index + " IccCardStatus: " + status);
 
         int slotId = status.mSlotPortMapping.mPhysicalSlotIndex;
-        if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
+        if (VDBG) log("onGetIccCardStatusDone: phoneId-" + index + " physicalSlotIndex " + slotId);
         if (slotId == INVALID_SLOT_ID) {
             slotId = index;
         }
@@ -1488,7 +1488,7 @@
         pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
         pw.println(" mHasBuiltInEuicc=" + mHasBuiltInEuicc);
         pw.println(" mHasActiveBuiltInEuicc=" + mHasActiveBuiltInEuicc);
-        pw.println(" mCardStrings=" + mCardStrings);
+        pw.println(" mCardStrings=" + Rlog.pii(LOG_TAG, mCardStrings));
         pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
         pw.println(" mPhoneIdToSlotId=" + Arrays.toString(mPhoneIdToSlotId));
         pw.println(" mUseRemovableEsimAsDefault=" + mUseRemovableEsimAsDefault);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index 0152dda..ab00cab 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -21,7 +21,6 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
-import android.telephony.SubscriptionInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -363,7 +362,6 @@
         pw.println(" this=" + this);
         pw.println(" mPortIdx=" + mPortIdx);
         pw.println(" mCi=" + mCi);
-        pw.println(" mIccid=" + SubscriptionInfo.givePrintableIccid(mIccid));
         pw.println(" mPhoneId=" + mPhoneId);
         pw.println(" mPhysicalSlotIndex=" + mPhysicalSlotIndex);
         synchronized (mOpenChannelRecords) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 2809895..5b84c6c 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -77,6 +77,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -1393,7 +1394,7 @@
         Set<String> uninstalledCarrierPackages = new ArraySet<>();
         List<UiccAccessRule> accessRules = rules.getAccessRules();
         for (UiccAccessRule accessRule : accessRules) {
-            String certHexString = accessRule.getCertificateHexString().toUpperCase();
+            String certHexString = accessRule.getCertificateHexString().toUpperCase(Locale.ROOT);
             String pkgName = certPackageMap.get(certHexString);
             if (!TextUtils.isEmpty(pkgName) && !isPackageBundled(mContext, pkgName)) {
                 uninstalledCarrierPackages.add(pkgName);
@@ -1423,7 +1424,7 @@
             String[] keyValue = keyValueString.split(keyValueDelim);
 
             if (keyValue.length == 2) {
-                map.put(keyValue[0].toUpperCase(), keyValue[1]);
+                map.put(keyValue[0].toUpperCase(Locale.ROOT), keyValue[1]);
             } else {
                 loge("Incorrect length of key-value pair in carrier app allow list map.  "
                         + "Length should be exactly 2");
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index 9b5b315..8de7e01 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -108,7 +108,6 @@
      * Update slot. The main trigger for this is a change in the ICC Card status.
      */
     public void update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex) {
-        if (DBG) log("cardStatus update: " + ics.toString());
         synchronized (mLock) {
             mPortIdxToPhoneId.put(ics.mSlotPortMapping.mPortIndex, phoneId);
             CardState oldState = mCardState;
@@ -172,7 +171,6 @@
      * Update slot based on IccSlotStatus.
      */
     public void update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex) {
-        if (DBG) log("slotStatus update: " + iss.toString());
         synchronized (mLock) {
             IccSimPortInfo[] simPortInfos = iss.mSimPortInfos;
             CardState oldState = mCardState;
@@ -610,7 +608,7 @@
                 + isMultipleEnabledProfileSupported());
         pw.println(" mIsRemovable=" + mIsRemovable);
         pw.println(" mLastRadioState=" + mLastRadioState);
-        pw.println(" mIccIds=" + getPrintableIccIds());
+        pw.println(" mIccIds=" + Rlog.pii(TAG, getPrintableIccIds()));
         pw.println(" mPortIdxToPhoneId=" + mPortIdxToPhoneId);
         pw.println(" mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid));
         pw.println(" mCardState=" + mCardState);
diff --git a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
index ff8d63d..bc46f80 100755
--- a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
@@ -68,6 +68,7 @@
         case EF_FPLMN:
         case EF_LRPLMNSI:
         case EF_HPPLMN:
+        case EF_SMSS:
             return MF_SIM + DF_ADF;
 
         case EF_PBR:
diff --git a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
index fc58d3c..ea2bf42 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
@@ -157,4 +157,8 @@
     protected Object[] getValues() {
         return UsimService.values();
     }
+
+    public byte[] getUSIMServiceTable() {
+        return mServiceTable;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
index 45bc06d..e797107 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
@@ -101,9 +101,6 @@
     private static final String DEV_CAP_NR5GC = "nr5gc";
     private static final String DEV_CAP_EUTRAN5GC = "eutran5gc";
 
-    private static final String ATR_ESIM_OS_V_M5 =
-            "3B9F97C00AB1FE453FC6838031E073FE211F65D002341569810F21";
-
     // These interfaces are used for simplifying the code by leveraging lambdas.
     private interface ApduRequestBuilder {
         void build(RequestBuilder requestBuilder)
@@ -128,7 +125,6 @@
     private volatile String mEid;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public boolean mIsSupportsMultipleEnabledProfiles;
-    private String mAtr;
 
     public EuiccPort(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock,
             UiccCard card, boolean isSupportsMultipleEnabledProfiles) {
@@ -141,7 +137,6 @@
             mEid = ics.eid;
             mCardId = ics.eid;
         }
-        mAtr = ics.atr;
         mIsSupportsMultipleEnabledProfiles = isSupportsMultipleEnabledProfiles;
     }
 
@@ -165,7 +160,6 @@
             if (!TextUtils.isEmpty(ics.eid)) {
                 mEid = ics.eid;
             }
-            mAtr = ics.atr;
             super.update(c, ci, ics, uiccCard);
         }
     }
@@ -187,13 +181,8 @@
      * @since 1.1.0 [GSMA SGP.22]
      */
     public void getAllProfiles(AsyncResultCallback<EuiccProfileInfo[]> callback, Handler handler) {
-        byte[] profileTags;
-        if (mIsSupportsMultipleEnabledProfiles) {
-            profileTags = ATR_ESIM_OS_V_M5.equals(mAtr)
-                    ? Tags.EUICC_PROFILE_MEP_TAGS : Tags.EUICC_PROFILE_MEP_TAGS_WITH_9F20;
-        } else {
-            profileTags = Tags.EUICC_PROFILE_TAGS;
-        }
+        byte[] profileTags = mIsSupportsMultipleEnabledProfiles ? Tags.EUICC_PROFILE_MEP_TAGS
+                : Tags.EUICC_PROFILE_TAGS;
         sendApdu(
                 newRequestProvider((RequestBuilder requestBuilder) ->
                         requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES)
@@ -234,13 +223,8 @@
      */
     public final void getProfile(String iccid, AsyncResultCallback<EuiccProfileInfo> callback,
             Handler handler) {
-        byte[] profileTags;
-        if (mIsSupportsMultipleEnabledProfiles) {
-            profileTags = ATR_ESIM_OS_V_M5.equals(mAtr)
-                    ? Tags.EUICC_PROFILE_MEP_TAGS : Tags.EUICC_PROFILE_MEP_TAGS_WITH_9F20;
-        } else {
-            profileTags = Tags.EUICC_PROFILE_TAGS;
-        }
+        byte[] profileTags = mIsSupportsMultipleEnabledProfiles ? Tags.EUICC_PROFILE_MEP_TAGS
+                : Tags.EUICC_PROFILE_TAGS;
         sendApdu(
                 newRequestProvider((RequestBuilder requestBuilder) ->
                         requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES)
@@ -1260,10 +1244,8 @@
             // if the Profile is in the Enabled state on the same eSIM Port as where this
             // getProfilesInfo command was sent. So should check for enabledOnEsimPort(TAG_PORT)
             // tag and verify its value is a valid port (means port value is >=0) or not.
-            if ((profileNode.hasChild(Tags.TAG_PORT)
-                    && profileNode.getChild(Tags.TAG_PORT).asInteger() >= 0)
-                    || (profileNode.hasChild(Tags.TAG_PORT_9F20)
-                    && profileNode.getChild(Tags.TAG_PORT_9F20).asInteger() >= 0)) {
+            if (profileNode.hasChild(Tags.TAG_PORT)
+                    && profileNode.getChild(Tags.TAG_PORT).asInteger() >= 0) {
                 profileBuilder.setState(EuiccProfileInfo.PROFILE_STATE_ENABLED);
             } else {
                 // noinspection WrongConstant
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/Tags.java b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
index 3aa9ef9..befc7f9 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
@@ -84,10 +84,7 @@
     static final int TAG_PROFILE_NAME = 0x92;
     static final int TAG_OPERATOR_ID = 0xB7;
     static final int TAG_CARRIER_PRIVILEGE_RULES = 0xBF76;
-    // TODO: PORT TAG(9F20 OR 9F24) will be used based on ATR Strings because not all MEP capable
-    //  devices have M5 OS. Once modem team is ready, revert back to 9F24 TAG only.
     static final int TAG_PORT = 0x9F24;
-    static final int TAG_PORT_9F20 = 0x9F20;
 
     // Tags from the RefArDo data standard - https://source.android.com/devices/tech/config/uicc
     static final int TAG_REF_AR_DO = 0xE2;
@@ -129,22 +126,5 @@
             (byte) (TAG_PORT % 256),
     };
 
-    // TAG list for Euicc Profile with 9F20 tag.
-    // TODO: This is temporary change, should be removed once all devices are upgraded to M5 OS.
-    static final byte[] EUICC_PROFILE_MEP_TAGS_WITH_9F20 = new byte[] {
-            TAG_ICCID,
-            (byte) TAG_NICKNAME,
-            (byte) TAG_SERVICE_PROVIDER_NAME,
-            (byte) TAG_PROFILE_NAME,
-            (byte) TAG_OPERATOR_ID,
-            (byte) (TAG_PROFILE_STATE / 256),
-            (byte) (TAG_PROFILE_STATE % 256),
-            (byte) TAG_PROFILE_CLASS,
-            (byte) TAG_PROFILE_POLICY_RULE,
-            (byte) (TAG_CARRIER_PRIVILEGE_RULES / 256),
-            (byte) (TAG_CARRIER_PRIVILEGE_RULES % 256),
-            (byte) (TAG_PORT_9F20 / 256),
-            (byte) (TAG_PORT_9F20 % 256),
-    };
     private Tags() {}
 }
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
index 17f2611..da429b7 100644
--- a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -16,12 +16,11 @@
 
 package com.android.internal.telephony.util;
 
-import com.android.internal.telephony.ImsSmsDispatcher;
-import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.ImsSmsDispatcher;
 import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 
 /**
@@ -95,6 +94,46 @@
     }
 
     /**
+     * Trigger the proper implementation for getting submit pdu for text sms based on format.
+     *
+     * @param isCdma true if cdma format should be used.
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destAddr the address to send the message to
+     * @param message the body of the message.
+     * @param statusReportRequested whether or not a status report is requested.
+     * @param smsHeader message header.
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     * @param messageRef TP Message Reference number
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+            String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader,
+            int priority, int validityPeriod, int messageRef) {
+        if (isCdma) {
+            return getSubmitPduCdma(scAddr, destAddr, message, statusReportRequested, smsHeader,
+                    priority);
+        } else {
+            return getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
+                    validityPeriod, messageRef);
+        }
+    }
+
+    /**
      * Gsm implementation for
      * {@link #getSubmitPdu(boolean, String, String, String, boolean)}
      *
@@ -132,6 +171,28 @@
     }
 
     /**
+     * Gsm implementation for
+     * {@link #getSubmitPdu(boolean, String, String, String, boolean, int)}
+     *
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destAddr the address to send the message to
+     * @param message the body of the message.
+     * @param statusReportRequested whether or not a status report is requested.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     * @param messageRef TP Message Reference number
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, int validityPeriod, int messageRef) {
+        return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr, message,
+                statusReportRequested, null, 0, 0, 0, validityPeriod, messageRef);
+    }
+
+    /**
      * Cdma implementation for
      * {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
      *
@@ -197,6 +258,29 @@
     }
 
     /**
+     * Trigger the proper implementation for getting submit pdu for data sms based on format.
+     *
+     * @param isCdma true if cdma format should be used.
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param message the body of the message to send
+     * @param statusReportRequested whether or not a status report is requested.
+     * @param messageRef TP Message Reference number
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+            String destAddr, int destPort, byte[] message, boolean statusReportRequested,
+            int messageRef) {
+        if (isCdma) {
+            return getSubmitPduCdma(scAddr, destAddr, destPort, message, statusReportRequested);
+        } else {
+            return getSubmitPduGsm(scAddr, destAddr, destPort, message, statusReportRequested,
+                    messageRef);
+        }
+    }
+
+    /**
      * Cdma implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
 
      * @param destAddr the address to send the message to
@@ -230,6 +314,22 @@
     }
 
     /**
+     * Gsm implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
+     *
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param message the body of the message to send
+     * @param statusReportRequested whether or not a status report is requested.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+        return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr,
+                destPort, message, statusReportRequested, messageRef);
+    }
+
+    /**
      * Calculate the number of septets needed to encode the message. This function should only be
      * called for individual segments of multipart message.
      *
diff --git a/tests/telephonytests/assets/eccdata b/tests/telephonytests/assets/eccdata
index 4c5959b..bd9ff47 100644
--- a/tests/telephonytests/assets/eccdata
+++ b/tests/telephonytests/assets/eccdata
Binary files differ
diff --git a/tests/telephonytests/assets/eccdata_input.txt b/tests/telephonytests/assets/eccdata_input.txt
index 8dcfa75..6f8632c 100644
--- a/tests/telephonytests/assets/eccdata_input.txt
+++ b/tests/telephonytests/assets/eccdata_input.txt
@@ -10,6 +10,57 @@
     types: MOUNTAIN_RESCUE
     types: MIEC
     types: AIEC
+    routing: NORMAL
+    normal_routing_mncs: "05"
+  }
+  eccs {
+    phone_number: "888"
+    types: POLICE
+    types: AMBULANCE
+    types: FIRE
+    types: MARINE_GUARD
+    types: MOUNTAIN_RESCUE
+    types: MIEC
+    types: AIEC
+    routing: NORMAL
+    normal_routing_mncs: "45"
+    normal_routing_mncs: "47"
+    normal_routing_mncs: "05"
+  }
+  eccs {
+    phone_number: "654321"
+    types: POLICE
+    types: AMBULANCE
+    types: FIRE
+    types: MARINE_GUARD
+    types: MOUNTAIN_RESCUE
+    types: MIEC
+    types: AIEC
+    routing: EMERGENCY
+    normal_routing_mncs: ""
+  }
+  eccs {
+    phone_number: "7654321"
+    types: POLICE
+    types: AMBULANCE
+    types: FIRE
+    types: MARINE_GUARD
+    types: MOUNTAIN_RESCUE
+    types: MIEC
+    types: AIEC
+    routing: NORMAL
+    normal_routing_mncs: ""
+  }
+  eccs {
+    phone_number: "4321"
+    types: POLICE
+    types: AMBULANCE
+    types: FIRE
+    types: MARINE_GUARD
+    types: MOUNTAIN_RESCUE
+    types: MIEC
+    types: AIEC
+    routing: NORMAL
   }
   ecc_fallback: "911"
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java
new file mode 100644
index 0000000..18d054d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallWaitingControllerTest.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+
+import static com.android.internal.telephony.CallWaitingController.KEY_CS_SYNC;
+import static com.android.internal.telephony.CallWaitingController.KEY_STATE;
+import static com.android.internal.telephony.CallWaitingController.KEY_SUB_ID;
+import static com.android.internal.telephony.CallWaitingController.PREFERENCE_TBCW;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED;
+import static com.android.internal.telephony.CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CallWaitingControllerTest extends TelephonyTest {
+    private static final int FAKE_SUB_ID = 1;
+
+    private static final int GET_DONE = 1;
+
+    private CallWaitingController mCWC;
+    private GetTestHandler mHandler;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(this.getClass().getSimpleName());
+        mSimulatedCommands.setRadioPower(true, null);
+        mPhone.mCi = this.mSimulatedCommands;
+        doReturn(FAKE_SUB_ID).when(mPhone).getSubId();
+
+        mCWC = new CallWaitingController(mPhone);
+        logd("CallWaitingController initiated, waiting for Power on");
+        /* Make sure radio state is power on before dial.
+         * When radio state changed from off to on, CallTracker
+         * will poll result from RIL. Avoid dialing triggered at the same*/
+        processAllMessages();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mCWC = null;
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testSetTerminalBasedCallWaitingSupported() {
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+    }
+
+    @Test
+    @SmallTest
+    public void testInitialize() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_NONE);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_NONE);
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        bundle = getConfigBundle(false, CALL_WAITING_SYNC_NONE, false);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierConfigChanged() {
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+
+        bundle = getConfigBundle(false, CALL_WAITING_SYNC_NONE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, false);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_SUPPORTED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+    }
+
+
+    private static class GetTestHandler extends Handler {
+        public int[] resp;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case GET_DONE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    resp = (int[]) ar.result;
+                    break;
+                default:
+            }
+        }
+
+        public void reset() {
+            resp = null;
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetCallWaitingSyncNone() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        assertFalse(mCWC.getCallWaiting(null));
+
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        mHandler = new GetTestHandler();
+
+        assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+        mHandler.reset();
+
+        assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetCallWaitingSyncNone() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_NONE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE | SERVICE_CLASS_DATA, null));
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+        assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_DATA, null));
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+        assertFalse(mCWC.setCallWaiting(true, SERVICE_CLASS_NONE, null));
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+    }
+
+    @Test
+    @SmallTest
+    public void testSyncUserChange() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_USER_CHANGE);
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_USER_CHANGE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        mHandler = new GetTestHandler();
+
+        mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+        mHandler.reset();
+
+        mSimulatedCommands.setCallWaiting(true, SERVICE_CLASS_VOICE, null);
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        mHandler.reset();
+
+        assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+    }
+
+    @Test
+    @SmallTest
+    public void testSyncFirstPowerUp() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_FIRST_POWER_UP);
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_FIRST_POWER_UP, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+        assertFalse(mCWC.getSyncState());
+
+        mCWC.notifyRegisteredToNetwork();
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getSyncState());
+    }
+
+    @Test
+    @SmallTest
+    public void testSyncFirstChange() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_NOT_ACTIVATED, CALL_WAITING_SYNC_FIRST_CHANGE);
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_FIRST_CHANGE, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+        mCWC.setImsRegistrationState(false);
+
+        assertFalse(mCWC.getSyncState());
+
+        mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+        mCWC.getCallWaiting(null);
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mCWC.getSyncState());
+
+        mSimulatedCommands.setCallWaiting(true, SERVICE_CLASS_VOICE, null);
+        mCWC.getCallWaiting(null);
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getSyncState());
+
+        assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mSimulatedCommands.mCallWaitActivated);
+
+        assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        // Local setting changed, but no change in CS network.
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mSimulatedCommands.mCallWaitActivated);
+    }
+
+    @Test
+    @SmallTest
+    public void testSyncImsOnly() {
+        mCWC.setTerminalBasedCallWaitingSupported(false);
+        setPreference(mPhone.getPhoneId(), FAKE_SUB_ID,
+                TERMINAL_BASED_ACTIVATED, CALL_WAITING_SYNC_IMS_ONLY);
+        mCWC.setTerminalBasedCallWaitingSupported(true);
+        PersistableBundle bundle = getConfigBundle(true, CALL_WAITING_SYNC_IMS_ONLY, true);
+        mCWC.updateCarrierConfig(FAKE_SUB_ID, bundle, true);
+
+        mSimulatedCommands.setCallWaiting(false, SERVICE_CLASS_VOICE, null);
+
+        // IMS is registered
+        mCWC.setImsRegistrationState(true);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        mHandler = new GetTestHandler();
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        // result carries the service state from IMS service
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        mHandler.reset();
+
+        // IMS is not registered
+        mCWC.setImsRegistrationState(false);
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        // result carries the service state from CS
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_NOT_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_NONE, mHandler.resp[1]);
+
+        // service state not synchronized between CS and IMS
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_ACTIVATED);
+
+        mHandler.reset();
+
+        // IMS is registered
+        mCWC.setImsRegistrationState(true);
+
+        assertTrue(mCWC.setCallWaiting(false, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+
+        // IMS is not registered
+        mCWC.setImsRegistrationState(false);
+
+        assertTrue(mCWC.setCallWaiting(true, SERVICE_CLASS_VOICE, null));
+        mTestableLooper.processAllMessages();
+
+        assertTrue(mCWC.getCallWaiting(mHandler.obtainMessage(GET_DONE)));
+        mTestableLooper.processAllMessages();
+
+        // result carries the service state from CS
+        assertNotNull(mHandler.resp);
+        assertEquals(2, mHandler.resp.length);
+        assertEquals(TERMINAL_BASED_ACTIVATED, mHandler.resp[0]);
+        assertEquals(SERVICE_CLASS_VOICE, mHandler.resp[1]);
+
+        // service state not synchronized between CS and IMS
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(false) == TERMINAL_BASED_NOT_ACTIVATED);
+        assertTrue(mCWC.getTerminalBasedCallWaitingState(true) == TERMINAL_BASED_NOT_SUPPORTED);
+        assertTrue(retrieveStatePreference(mPhone.getSubId()) == TERMINAL_BASED_NOT_ACTIVATED);
+    }
+
+    private PersistableBundle getConfigBundle(boolean provisioned,
+            int preference, boolean defaultState) {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
+                provisioned ? new int[] { SUPPLEMENTARY_SERVICE_CW } : new int[] { });
+        bundle.putInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, preference);
+        bundle.putBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL, defaultState);
+        return bundle;
+    }
+
+    private int retrieveStatePreference(int subId) {
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+        return sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+    }
+
+    private void setPreference(int phoneId, int subId, int state, int syncPreference) {
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putInt(KEY_SUB_ID + phoneId, subId);
+        editor.putInt(KEY_STATE + subId, state);
+        editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
+        editor.apply();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 782f78e..3be8509 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -24,6 +24,7 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.provider.BaseColumns;
 import android.provider.Telephony;
 import android.telephony.SubscriptionManager;
@@ -123,6 +124,10 @@
                     + Telephony.SimInfo.COLUMN_PORT_INDEX + " INTEGER DEFAULT -1,"
                     + Telephony.SimInfo.COLUMN_USAGE_SETTING + " INTEGER DEFAULT "
                             + SubscriptionManager.USAGE_SETTING_UNKNOWN
+                    + "," + Telephony.SimInfo.COLUMN_TP_MESSAGE_REF
+                    + "  INTEGER DEFAULT -1,"
+                    + Telephony.SimInfo.COLUMN_USER_HANDLE + " INTEGER DEFAULT "
+                    + UserHandle.USER_NULL
                     + ");";
         }
 
@@ -143,6 +148,7 @@
 
     @Override
     public Uri insert(Uri uri, ContentValues values) {
+        Log.d(TAG, "insert. values=" + values);
         SQLiteDatabase db = mDbHelper.getWritableDatabase();
         long id = db.insert("siminfo", null, values);
         return ContentUris.withAppendedId(Telephony.SimInfo.CONTENT_URI, id);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
new file mode 100644
index 0000000..2c48158
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.telephony.uicc.AdnRecord;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class FdnUtilsTest {
+
+    private ArrayList<AdnRecord> initializeFdnList() {
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        AdnRecord adnRecord = new AdnRecord(null, null);
+        // By default, every sim card holds 15 empty FDN records
+        int fdnListSize = 15;
+        for (int i = 0; i < fdnListSize; i++) {
+            fdnList.add(adnRecord);
+        }
+        return fdnList;
+    }
+
+    @Test
+    public void fdnListIsNull_returnsFalse() {
+        assertFalse(FdnUtils.isFDN( "123456789", "US", null));
+    }
+
+    @Test
+    public void fdnListIsEmpty_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+    }
+
+    @Test
+    public void fdnListHasOnlyDefaultRecords_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+
+        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+    }
+
+    @Test
+    public void fdnListHasRecordWithEmptyNumberStr_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "");
+        fdnList.add(1, adnRecord);
+
+        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrInFdnList_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "123456789");
+        fdnList.add(2, adnRecord);
+
+        assertTrue(FdnUtils.isFDN( "123456789", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrNotInFdnList_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "111111111");
+        fdnList.add(3, adnRecord);
+
+        assertFalse(FdnUtils.isFDN("123456788", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrIsNull_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "111111111");
+        fdnList.add(4, adnRecord);
+
+        assertFalse(FdnUtils.isFDN( null, "US", fdnList));
+    }
+
+    @Test
+    public void fdnEntryFirstSubStringOfDialStr_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "123");
+        fdnList.add(5, adnRecord);
+
+        assertTrue(FdnUtils.isFDN( "12345", "US", fdnList));
+    }
+
+    @Test
+    public void fdnEntrySubStringOfDialStr_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "123");
+        fdnList.add(5, adnRecord);
+
+        assertFalse(FdnUtils.isFDN("612345", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrFirstSubStringOfFdnEntry_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "12345");
+        fdnList.add(5, adnRecord);
+
+        assertFalse(FdnUtils.isFDN("123", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrSubStringOfFdnEntry_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "612345");
+        fdnList.add(5, adnRecord);
+
+        assertFalse(FdnUtils.isFDN("123", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrWithoutCountryCode_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "+16502910000");
+        fdnList.add(6, adnRecord);
+
+        assertTrue(FdnUtils.isFDN( "6502910000", "US", fdnList));
+    }
+
+    @Test
+    public void dialStrWithCountryCode_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "6502910000");
+        fdnList.add(6, adnRecord);
+
+        assertTrue(FdnUtils.isFDN("+16502910000", "US", fdnList));
+    }
+
+    @Test
+    public void defaultCountryIsoIsEmpty_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "650");
+        fdnList.add(6, adnRecord);
+
+        assertTrue(FdnUtils.isFDN("+16502910000", "", fdnList));
+    }
+
+    @Test
+    public void defaultCountryIsoIsEmpty_returnsFalse() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "+1650");
+        fdnList.add(6, adnRecord);
+
+        assertFalse(FdnUtils.isFDN("6502910000", "", fdnList));
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 9e45cc3..ba5e155 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CLIR_SUPPRESSION;
 import static com.android.internal.telephony.Phone.EVENT_ICC_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_SRVCC_STATE_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED;
@@ -77,8 +78,11 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.test.SimulatedCommandsVerifier;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
 import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccConstants;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccVmNotSupportedException;
 import com.android.internal.telephony.uicc.UiccController;
@@ -107,6 +111,7 @@
     private Handler mTestHandler;
     private UiccSlot mUiccSlot;
     private CommandsInterface mMockCi;
+    private AdnRecordCache adnRecordCache;
 
     //mPhoneUnderTest
     private GsmCdmaPhone mPhoneUT;
@@ -138,6 +143,7 @@
         mUiccSlot = Mockito.mock(UiccSlot.class);
         mUiccPort = Mockito.mock(UiccPort.class);
         mMockCi = Mockito.mock(CommandsInterface.class);
+        adnRecordCache = Mockito.mock(AdnRecordCache.class);
         doReturn(false).when(mSST).isDeviceShuttingDown();
         doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
 
@@ -1291,11 +1297,12 @@
     public void testSetRadioPower() throws Exception {
         mPhoneUT.setRadioPower(false);
         verify(mSST).setRadioPowerForReason(false, false, false, false,
-                Phone.RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
 
         // Turn on radio for emergency call.
         mPhoneUT.setRadioPower(true, true, false, true);
-        verify(mSST).setRadioPowerForReason(true, true, false, true, Phone.RADIO_POWER_REASON_USER);
+        verify(mSST).setRadioPowerForReason(true, true, false, true,
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     @Test
@@ -1303,12 +1310,12 @@
     public void testSetRadioPowerOnForTestEmergencyCall() {
         mPhoneUT.setRadioPower(false);
         verify(mSST).setRadioPowerForReason(false, false, false, false,
-                Phone.RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
 
         mPhoneUT.setRadioPowerOnForTestEmergencyCall(false);
         verify(mSST).clearAllRadioOffReasons();
         verify(mSST).setRadioPowerForReason(eq(true), eq(false), anyBoolean(), eq(false),
-                eq(Phone.RADIO_POWER_REASON_USER));
+                eq(TelephonyManager.RADIO_POWER_REASON_USER));
     }
 
     @Test
@@ -1599,7 +1606,7 @@
 
         doReturn(true).when(mTelephonyManager).isEmergencyNumber(anyString());
         doReturn(isEmergencyPerDialedSim).when(mEmergencyNumberTracker).isEmergencyNumber(
-                anyString(), anyBoolean());
+                anyString());
 
         mPhoneUT.setImsPhone(mImsPhone);
     }
@@ -1651,10 +1658,23 @@
 
 
     private SubscriptionInfo makeSubscriptionInfo(boolean isOpportunistic, int usageSetting) {
-        return new SubscriptionInfo(
-                 1, "xxxxxxxxx", 1, "Android Test", "Android Test", 0, 0, "8675309", 0,
-                null, "001", "01", "us", true, null, null, 0, isOpportunistic, null, false,
-                1, 1, 0, null, null, true, 0, usageSetting);
+        return new SubscriptionInfo.Builder()
+                .setId(1)
+                .setIccId("xxxxxxxxx")
+                .setSimSlotIndex(1)
+                .setDisplayName("Android Test")
+                .setDisplayName("Android Test")
+                .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+                .setNumber("8675309")
+                .setMcc("001")
+                .setMnc("01")
+                .setCountryIso("us")
+                .setEmbedded(true)
+                .setOpportunistic(isOpportunistic)
+                .setCarrierId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING)
+                .setUsageSetting(usageSetting)
+                .build();
     }
 
     @Test
@@ -1726,4 +1746,544 @@
         processAllMessages();
         verify(mMockCi, never()).setUsageSetting(any(), anyInt());
     }
+
+    public void fdnCheckSetup() {
+        // FDN check setup
+        mPhoneUT.mCi = mMockCi;
+        doReturn(adnRecordCache).when(mSimRecords).getAdnCache();
+        doReturn(mUiccProfile).when(mUiccController).getUiccProfileForPhone(anyInt());
+        doReturn(true).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+        doReturn(true).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+    }
+
+    @Test
+    public void testGetCallForwardingOption_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*#21");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.getCallForwardingOption(CommandsInterface.CF_REASON_UNCONDITIONAL, message);
+        processAllMessages();
+        verify(mMockCi).queryCallForwardStatus(eq(CommandsInterface.CF_REASON_UNCONDITIONAL),
+                eq(CommandsInterface.SERVICE_CLASS_VOICE), any(), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.getCallForwardingOption(CommandsInterface.CF_REASON_UNCONDITIONAL, message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testSetCallForwardingOption_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "**21");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.setCallForwardingOption(CommandsInterface.CF_ACTION_REGISTRATION,
+                CommandsInterface.CF_REASON_UNCONDITIONAL, "123", 0,
+                message);
+        processAllMessages();
+        verify(mMockCi).setCallForward(eq(CommandsInterface.CF_ACTION_REGISTRATION),
+                eq(CommandsInterface.CF_REASON_UNCONDITIONAL),
+                eq(CommandsInterface.SERVICE_CLASS_VOICE), eq("123"), eq(0), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.setCallForwardingOption(CommandsInterface.CF_ACTION_REGISTRATION,
+                CommandsInterface.CF_REASON_UNCONDITIONAL, "", 0, message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testGetCallBarring_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*#330");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, "", message,
+                CommandsInterface.SERVICE_CLASS_VOICE);
+        processAllMessages();
+        verify(mMockCi).queryFacilityLock(eq(CommandsInterface.CB_FACILITY_BA_ALL), any(),
+                eq(CommandsInterface.SERVICE_CLASS_VOICE), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, "", message,
+                CommandsInterface.SERVICE_CLASS_VOICE);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testSetCallBarring_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*330");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, true, "",
+                message, CommandsInterface.SERVICE_CLASS_VOICE);
+        processAllMessages();
+        verify(mMockCi).setFacilityLock(eq(CommandsInterface.CB_FACILITY_BA_ALL),
+                eq(true), any(), eq(CommandsInterface.SERVICE_CLASS_VOICE), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, true, "",
+                message, CommandsInterface.SERVICE_CLASS_VOICE);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testChangeCallBarringPassword_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "**03*330");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.changeCallBarringPassword(CommandsInterface.CB_FACILITY_BA_ALL, "",
+                "", message);
+        processAllMessages();
+        verify(mMockCi).changeBarringPassword(eq(CommandsInterface.CB_FACILITY_BA_ALL), any(),
+                any(), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.changeCallBarringPassword(CommandsInterface.CB_FACILITY_BA_ALL, "",
+                "", message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testGetOutgoingCallerIdDisplay_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*#31");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.getOutgoingCallerIdDisplay(message);
+        processAllMessages();
+        verify(mMockCi).getCLIR(any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.getOutgoingCallerIdDisplay(message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testSetOutgoingCallerIdDisplay_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*31");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, message);
+        processAllMessages();
+        verify(mMockCi).setCLIR(eq(CommandsInterface.CLIR_SUPPRESSION), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testQueryCLIP_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*#30");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.queryCLIP(message);
+        processAllMessages();
+        verify(mMockCi).queryCLIP(any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.queryCLIP(message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testGetCallWaiting_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*#43");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.getCallWaiting(message);
+        processAllMessages();
+        verify(mMockCi).queryCallWaiting(eq(CommandsInterface.SERVICE_CLASS_NONE), any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.getCallWaiting(message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testSetCallWaiting_FdnCheck() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+        Message message = Message.obtain(mTestHandler);
+
+        // FDN check success - no exception is returned
+        AdnRecord adnRecord = new AdnRecord(null, "*43");
+        fdnList.add(0, adnRecord);
+        mPhoneUT.setCallWaiting(true, CommandsInterface.SERVICE_CLASS_VOICE, message);
+        processAllMessages();
+        verify(mMockCi).setCallWaiting(eq(true), eq(CommandsInterface.SERVICE_CLASS_VOICE),
+                any());
+        // FDN check failure - returns CommandException in onComplete
+        fdnList.remove(0);
+        mPhoneUT.setCallWaiting(true, CommandsInterface.SERVICE_CLASS_VOICE, message);
+        processAllMessages();
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(((CommandException) ar.exception).getCommandError(),
+                CommandException.Error.FDN_CHECK_FAILURE);
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void testDial_fdnCheck() throws Exception{
+        // dial setup
+        mSST.mSS = mServiceState;
+        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+        mCT.mForegroundCall = mGsmCdmaCall;
+        mCT.mBackgroundCall = mGsmCdmaCall;
+        mCT.mRingingCall = mGsmCdmaCall;
+        doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();
+        replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(adnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+        // FDN check success - no exception is returned
+        AdnRecord dialRecord = new AdnRecord(null, "1234567890");
+        fdnList.add(0, dialRecord);
+        Connection connection = mPhoneUT.dial("1234567890",
+                new PhoneInternalInterface.DialArgs.Builder().build());
+        verify(mCT).dialGsm(eq("1234567890"), any(PhoneInternalInterface.DialArgs.class));
+
+        // FDN check failure - returns CallStateException
+        fdnList.remove(0);
+        try {
+            connection = mPhoneUT.dial("1234567890",
+                    new PhoneInternalInterface.DialArgs.Builder().build());
+            fail("Expected CallStateException with ERROR_FDN_BLOCKED thrown.");
+        } catch(CallStateException e) {
+            assertEquals(CallStateException.ERROR_FDN_BLOCKED, e.getError());
+        }
+
+        // clean up
+        fdnCheckCleanup();
+    }
+
+    public void fdnCheckCleanup() {
+        doReturn(false).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+        doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateSsOverUtSupported() throws Exception {
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+        // Ut is disabled in config
+        doReturn(false).when(mSsDomainController).useCfOverUt(anyInt());
+        doReturn(false).when(mSsDomainController).useCbOverUt(anyString());
+        doReturn(false).when(mSsDomainController).useSsOverUt(anyString());
+        replaceInstance(GsmCdmaPhone.class, "mSsDomainController", mPhoneUT, mSsDomainController);
+
+        mPhoneUT.getCallForwardingOption(0, 0, null);
+        verify(mImsPhone, times(0)).getCallForwardingOption(eq(0), eq(0), any());
+
+        mPhoneUT.setCallForwardingOption(0, 0, null, 0, 0, null);
+        verify(mImsPhone, times(0)).setCallForwardingOption(
+                eq(0), eq(0), any(), eq(0), eq(0), any());
+
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAIC, null, null, 0);
+        verify(mImsPhone, times(0)).getCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAIC), any(), any(), eq(0));
+
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOC, false, null, null, 0);
+        verify(mImsPhone, times(0)).setCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAOC), eq(false), any(), any(), eq(0));
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(0)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(0, null);
+        verify(mImsPhone, times(0)).setOutgoingCallerIdDisplay(eq(0), any());
+
+        mPhoneUT.queryCLIP(null);
+        verify(mImsPhone, times(0)).queryCLIP(any());
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(0)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(0)).setCallWaiting(eq(false), any());
+
+        // config changed but isUtEnabled still returns false
+        doReturn(true).when(mSsDomainController).useCfOverUt(anyInt());
+        doReturn(true).when(mSsDomainController).useCbOverUt(anyString());
+        doReturn(true).when(mSsDomainController).useSsOverUt(anyString());
+
+        mPhoneUT.getCallForwardingOption(0, 0, null);
+        verify(mImsPhone, times(0)).getCallForwardingOption(eq(0), eq(0), any());
+
+        mPhoneUT.setCallForwardingOption(0, 0, null, 0, 0, null);
+        verify(mImsPhone, times(0)).setCallForwardingOption(
+                eq(0), eq(0), any(), eq(0), eq(0), any());
+
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAIC, null, null, 0);
+        verify(mImsPhone, times(0)).getCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAIC), any(), any(), eq(0));
+
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOC, false, null, null, 0);
+        verify(mImsPhone, times(0)).setCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAOC), eq(false), any(), any(), eq(0));
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(0)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(0, null);
+        verify(mImsPhone, times(0)).setOutgoingCallerIdDisplay(eq(0), any());
+
+        mPhoneUT.queryCLIP(null);
+        verify(mImsPhone, times(0)).queryCLIP(any());
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(0)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(0)).setCallWaiting(eq(false), any());
+
+        // isUtEnabled returns true
+        doReturn(true).when(mSsDomainController).isUtEnabled();
+
+        mPhoneUT.getCallForwardingOption(0, 0, null);
+        verify(mImsPhone, times(1)).getCallForwardingOption(eq(0), eq(0), any());
+
+        mPhoneUT.setCallForwardingOption(0, 0, null, 0, 0, null);
+        verify(mImsPhone, times(1)).setCallForwardingOption(
+                eq(0), eq(0), any(), eq(0), eq(0), any());
+
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAIC, null, null, 0);
+        verify(mImsPhone, times(1)).getCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAIC), any(), any(), eq(0));
+
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOC, false, null, null, 0);
+        verify(mImsPhone, times(1)).setCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAOC), eq(false), any(), any(), eq(0));
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(1)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(0, null);
+        verify(mImsPhone, times(1)).setOutgoingCallerIdDisplay(eq(0), any());
+
+        mPhoneUT.queryCLIP(null);
+        verify(mImsPhone, times(1)).queryCLIP(any());
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(1)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(1)).setCallWaiting(eq(false), any());
+
+        // configure changed, not support Ut
+        doReturn(false).when(mSsDomainController).useCfOverUt(anyInt());
+        doReturn(false).when(mSsDomainController).useCbOverUt(anyString());
+        doReturn(false).when(mSsDomainController).useSsOverUt(anyString());
+
+        // no change in interfactiion counts
+        mPhoneUT.getCallForwardingOption(0, 0, null);
+        verify(mImsPhone, times(1)).getCallForwardingOption(eq(0), eq(0), any());
+
+        mPhoneUT.setCallForwardingOption(0, 0, null, 0, 0, null);
+        verify(mImsPhone, times(1)).setCallForwardingOption(
+                eq(0), eq(0), any(), eq(0), eq(0), any());
+
+        mPhoneUT.getCallBarring(CommandsInterface.CB_FACILITY_BAIC, null, null, 0);
+        verify(mImsPhone, times(1)).getCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAIC), any(), any(), eq(0));
+
+        mPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOC, false, null, null, 0);
+        verify(mImsPhone, times(1)).setCallBarring(
+                eq(CommandsInterface.CB_FACILITY_BAOC), eq(false), any(), any(), eq(0));
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(1)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(0, null);
+        verify(mImsPhone, times(1)).setOutgoingCallerIdDisplay(eq(0), any());
+
+        mPhoneUT.queryCLIP(null);
+        verify(mImsPhone, times(1)).queryCLIP(any());
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(1)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(1)).setCallWaiting(eq(false), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testOemHandlesTerminalBasedCallWaiting() throws Exception {
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+        // Ut is disabled in config
+        doReturn(false).when(mSsDomainController).useSsOverUt(anyString());
+        doReturn(false).when(mSsDomainController).getOemHandlesTerminalBasedCallWaiting();
+
+        replaceInstance(GsmCdmaPhone.class, "mSsDomainController", mPhoneUT, mSsDomainController);
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(0)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(0)).setCallWaiting(eq(false), any());
+
+        // OEM handles the terminal-based call waiting service by itself.
+        doReturn(true).when(mSsDomainController).getOemHandlesTerminalBasedCallWaiting();
+
+        mPhoneUT.getCallWaiting(null);
+        verify(mImsPhone, times(1)).getCallWaiting(any());
+
+        mPhoneUT.setCallWaiting(false, CommandsInterface.SERVICE_CLASS_VOICE, null);
+        verify(mImsPhone, times(1)).setCallWaiting(eq(false), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testOemHandlesTerminalBasedClir() throws Exception {
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+
+        // Ut is disabled in config
+        doReturn(false).when(mSsDomainController).useSsOverUt(anyString());
+        doReturn(false).when(mSsDomainController).getOemHandlesTerminalBasedClir();
+
+        replaceInstance(GsmCdmaPhone.class, "mSsDomainController", mPhoneUT, mSsDomainController);
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(0)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(CLIR_SUPPRESSION, null);
+        verify(mImsPhone, times(0)).setOutgoingCallerIdDisplay(anyInt(), any());
+
+        // OEM handles the terminal-based CLIR by itself.
+        doReturn(true).when(mSsDomainController).getOemHandlesTerminalBasedClir();
+
+        mPhoneUT.getOutgoingCallerIdDisplay(null);
+        verify(mImsPhone, times(1)).getOutgoingCallerIdDisplay(any());
+
+        mPhoneUT.setOutgoingCallerIdDisplay(CLIR_SUPPRESSION, null);
+        verify(mImsPhone, times(1)).setOutgoingCallerIdDisplay(eq(CLIR_SUPPRESSION), any());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index 6251560..3bbba0b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -225,4 +226,74 @@
                 ImsSmsImplBase.SEND_STATUS_ERROR, 0, 41);
         verify(mSmsTracker).onFailed(any(Context.class), anyInt(), eq(41));
     }
+
+    @Test
+    public void testSendSmswithMessageRef() throws Exception {
+        int token = mImsSmsDispatcher.mNextToken.get();
+        int messageRef = mImsSmsDispatcher.nextMessageRef() + 1;
+
+        when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+        when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+        mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+                null, null, null, null, false,
+                -1, false, -1, false, 0);
+        verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+                nullable(String.class), eq(false), (byte[]) any());
+    }
+
+    @Test
+    public void testFallbackGsmRetrywithMessageRef() throws Exception {
+        int token = mImsSmsDispatcher.mNextToken.get();
+        int messageRef = mImsSmsDispatcher.nextMessageRef() + 1;
+
+        when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+        when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+        mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+                null, null, null, null, false,
+                -1, false, -1, false, 0);
+        verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+                nullable(String.class), eq(false), (byte[]) any());
+
+        mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 1, messageRef,
+                ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, 0, SmsResponse.NO_ERROR_CODE);
+        processAllMessages();
+
+        ArgumentCaptor<SMSDispatcher.SmsTracker> captor =
+                ArgumentCaptor.forClass(SMSDispatcher.SmsTracker.class);
+        verify(mSmsDispatchersController).sendRetrySms(captor.capture());
+        assertTrue(messageRef + 1 == captor.getValue().mMessageRef);
+    }
+
+    @Test
+    public void testErrorImsRetrywithMessageRef() throws Exception {
+        int token = mImsSmsDispatcher.mNextToken.get();
+        int messageRef = mImsSmsDispatcher.nextMessageRef() + 1;
+        when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
+        when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
+
+        mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
+                null, null, null, null, false,
+                -1, false, -1, false, 0);
+        verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+                nullable(String.class), eq(false), (byte[]) any());
+
+        // Retry over IMS
+        mImsSmsDispatcher.getSmsListener().onSendSmsResult(token + 1, messageRef,
+                ImsSmsImplBase.SEND_STATUS_ERROR_RETRY, 0, SmsResponse.NO_ERROR_CODE);
+        waitForMs(SMSDispatcher.SEND_RETRY_DELAY + 200);
+        processAllMessages();
+
+        // Make sure tpmr value is same and retry bit set
+        ArgumentCaptor<byte[]> byteCaptor = ArgumentCaptor.forClass(byte[].class);
+        verify(mImsManager).sendSms(eq(token + 2), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
+                nullable(String.class), eq(true), byteCaptor.capture());
+        byte[] pdu = byteCaptor.getValue();
+        assertEquals(messageRef, pdu[1]);
+        assertEquals(0x04, (pdu[0] & 0x04));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index 5af1c1f..ac76797 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -55,7 +55,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
+import com.android.internal.telephony.data.DataSettingsManager;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -75,8 +75,8 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class MultiSimSettingControllerTest extends TelephonyTest {
-    private static final int SINGLE_SIM = 1;
     private static final int DUAL_SIM = 2;
+    private static final String PHONE_PACKAGE = "com.android.internal.telephony";
     private MultiSimSettingController mMultiSimSettingControllerUT;
     private Phone[] mPhones;
     private ParcelUuid mGroupUuid1 = new ParcelUuid(UUID.randomUUID());
@@ -85,32 +85,42 @@
     private SubscriptionController mSubControllerMock;
     private Phone mPhoneMock1;
     private Phone mPhoneMock2;
-    private DataEnabledSettings mDataEnabledSettingsMock1;
-    private DataEnabledSettings mDataEnabledSettingsMock2;
+    private DataSettingsManager mDataSettingsManagerMock1;
+    private DataSettingsManager mDataSettingsManagerMock2;
     private CommandsInterface mMockCi;
 
+    private final SubscriptionInfo mSubInfo1 = new SubscriptionInfo.Builder()
+            .setId(1)
+            .setIccId("subInfo1 IccId")
+            .setSimSlotIndex(0)
+            .setDisplayName("T-mobile")
+            .setCarrierName("T-mobile")
+            .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+            .setIconTint(255)
+            .setNumber("12345")
+            .setMcc("310")
+            .setMnc("260")
+            .setCountryIso("us")
+            .build();
 
-    private final SubscriptionInfo mSubInfo1 = new SubscriptionInfo(1, "subInfo1 IccId", 0,
-            "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
-            "156", false, null, null);
+    private final SubscriptionInfo mSubInfo2 = new SubscriptionInfo.Builder(mSubInfo1)
+            .setId(2)
+            .setIccId("subInfo2 IccId")
+            .setGroupUuid(mGroupUuid1.toString())
+            .build();
 
-    private final SubscriptionInfo mSubInfo2 = new SubscriptionInfo(2, "subInfo2 IccId", 1,
-            "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
-            "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
-            TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
-            SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
+    private final SubscriptionInfo mSubInfo3 = new SubscriptionInfo.Builder(mSubInfo1)
+            .setId(3)
+            .setIccId("subInfo3 IccId")
+            .setGroupUuid(mGroupUuid1.toString())
+            .build();
 
-    private final SubscriptionInfo mSubInfo3 = new SubscriptionInfo(3, "subInfo3 IccId", -1,
-            "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
-            "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
-            TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
-            SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
+    private final SubscriptionInfo mSubInfo4 = new SubscriptionInfo.Builder(mSubInfo1)
+            .setId(4)
+            .setIccId("subInfo4 IccId")
+            .setGroupUuid(mGroupUuid1.toString())
+            .build();
 
-    private final SubscriptionInfo mSubInfo4 = new SubscriptionInfo(4, "subInfo4 IccId", -1,
-            "T-mobile", "T-mobile", 0, 255, "12345", 0, null, "310", "260",
-            "156", false, null, null, -1, false, mGroupUuid1.toString(), false,
-            TelephonyManager.UNKNOWN_CARRIER_ID, SubscriptionManager.PROFILE_CLASS_DEFAULT,
-            SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true, -1);
 
     @Before
     public void setUp() throws Exception {
@@ -118,8 +128,8 @@
         mSubControllerMock = mock(SubscriptionController.class);
         mPhoneMock1 = mock(Phone.class);
         mPhoneMock2 = mock(Phone.class);
-        mDataEnabledSettingsMock1 = mock(DataEnabledSettings.class);
-        mDataEnabledSettingsMock2 = mock(DataEnabledSettings.class);
+        mDataSettingsManagerMock1 = mock(DataSettingsManager.class);
+        mDataSettingsManagerMock2 = mock(DataSettingsManager.class);
         mMockCi = mock(CommandsInterface.class);
         // Default configuration:
         // DSDS device.
@@ -145,8 +155,8 @@
         doReturn(new int[]{1, 2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
 
         mPhones = new Phone[] {mPhoneMock1, mPhoneMock2};
-        doReturn(mDataEnabledSettingsMock1).when(mPhoneMock1).getDataEnabledSettings();
-        doReturn(mDataEnabledSettingsMock2).when(mPhoneMock2).getDataEnabledSettings();
+        doReturn(mDataSettingsManagerMock1).when(mPhoneMock1).getDataSettingsManager();
+        doReturn(mDataSettingsManagerMock2).when(mPhoneMock2).getDataSettingsManager();
 
         doReturn(Arrays.asList(mSubInfo1)).when(mSubControllerMock).getSubInfo(
                 eq(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1), any());
@@ -385,8 +395,8 @@
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
         processAllMessages();
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         // Enable on non-default sub should trigger setDefaultDataSubId.
         mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
@@ -397,8 +407,8 @@
         doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
         mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
         processAllMessages();
-        verify(mDataEnabledSettingsMock1).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock1).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
         doReturn(1).when(mSubControllerMock).getDefaultSmsSubId();
@@ -440,10 +450,10 @@
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
         processAllMessages();
-        verify(mDataEnabledSettingsMock1).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock1).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         // as a result of the above calls, update new values to be returned
         doReturn(false).when(mPhoneMock1).isUserDataEnabled();
@@ -458,8 +468,10 @@
         doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
         mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
         processAllMessages();
-        verify(mDataEnabledSettingsMock1, times(1)).setDataEnabled(anyInt(), anyBoolean());
-        verify(mDataEnabledSettingsMock2, times(1)).setDataEnabled(anyInt(), anyBoolean());
+        verify(mDataSettingsManagerMock1, times(1))
+                .setDataEnabled(anyInt(), anyBoolean(), anyString());
+        verify(mDataSettingsManagerMock2, times(1))
+                .setDataEnabled(anyInt(), anyBoolean(), anyString());
     }
 
     @Test
@@ -478,10 +490,10 @@
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
         processAllMessages();
-        verify(mDataEnabledSettingsMock1).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock1).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         // as a result of the above calls, update new values to be returned
         doReturn(false).when(mPhoneMock1).isUserDataEnabled();
@@ -529,8 +541,8 @@
         doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
         mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
         processAllMessages();
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
         mMultiSimSettingControllerUT.notifyUserDataEnabled(2, false);
         processAllMessages();
         assertFalse(GlobalSettingsHelper.getBoolean(
@@ -578,13 +590,13 @@
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
         processAllMessages();
         verify(mSubControllerMock).setDefaultDataSubId(2);
-        verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
-                anyInt(), anyBoolean());
+        verify(mDataSettingsManagerMock1, never()).setDataEnabled(
+                anyInt(), anyBoolean(), anyString());
         verifyDismissIntentSent();
 
         clearInvocations(mSubControllerMock);
-        clearInvocations(mDataEnabledSettingsMock1);
-        clearInvocations(mDataEnabledSettingsMock2);
+        clearInvocations(mDataSettingsManagerMock1);
+        clearInvocations(mDataSettingsManagerMock2);
         doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
         // Toggle data on sub 1 or sub 2. Nothing should happen as they are independent.
         mMultiSimSettingControllerUT.notifyUserDataEnabled(1, false);
@@ -595,10 +607,10 @@
         mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
         processAllMessages();
         verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
-        verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
-                eq(TelephonyManager.DATA_ENABLED_REASON_USER), anyBoolean());
-        verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock1, never()).setDataEnabled(
+                eq(TelephonyManager.DATA_ENABLED_REASON_USER), anyBoolean(), anyString());
+        verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+                eq(TelephonyManager.DATA_ENABLED_REASON_USER), eq(false), anyString());
     }
 
     private void verifyDismissIntentSent() {
@@ -613,7 +625,7 @@
     public void testGroupedCbrs() throws Exception {
         // Mark sub 1 as opportunistic.
         replaceInstance(SubscriptionInfo.class, "mIsOpportunistic", mSubInfo1, true);
-        replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
+        replaceInstance(SubscriptionInfo.class, "mGroupUuid", mSubInfo1, mGroupUuid1);
         doReturn(true).when(mSubControllerMock).isOpportunistic(1);
         // Make opportunistic sub 1 and sub 2 data enabled.
         doReturn(true).when(mPhoneMock1).isUserDataEnabled();
@@ -636,7 +648,8 @@
         mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
         processAllMessages();
         // This should result in setting sync.
-        verify(mDataEnabledSettingsMock1).setUserDataEnabled(false, false);
+        verify(mDataSettingsManagerMock1).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+                false, PHONE_PACKAGE);
         assertFalse(GlobalSettingsHelper.getBoolean(
                 mContext, Settings.Global.DATA_ROAMING, 1, true));
 
@@ -645,7 +658,8 @@
         // Turning data on on sub 2. Sub 1 should also be turned on.
         mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
         processAllMessages();
-        verify(mDataEnabledSettingsMock1).setUserDataEnabled(true, false);
+        verify(mDataSettingsManagerMock1).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+                true, PHONE_PACKAGE);
         verifyDismissIntentSent();
     }
 
@@ -653,7 +667,7 @@
     @SmallTest
     public void testGroupedPrimaryRemoved() throws Exception {
         // Create subscription grouping of subs 1 and 2.
-        replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
+        replaceInstance(SubscriptionInfo.class, "mGroupUuid", mSubInfo1, mGroupUuid1);
         doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(1);
         doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(2);
         doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
@@ -709,13 +723,14 @@
         processAllMessages();
 
         // Create subscription grouping.
-        replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
+        replaceInstance(SubscriptionInfo.class, "mGroupUuid", mSubInfo1, mGroupUuid1);
         doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
                 .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
         mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
         processAllMessages();
         // This should result in setting sync.
-        verify(mDataEnabledSettingsMock2).setUserDataEnabled(true, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+                true, PHONE_PACKAGE);
         assertFalse(GlobalSettingsHelper.getBoolean(
                 mContext, Settings.Global.DATA_ROAMING, 2, true));
         verify(mSubControllerMock).setDataRoaming(/*enable*/0, /*subId*/1);
@@ -724,7 +739,8 @@
         doReturn(false).when(mPhoneMock1).isUserDataEnabled();
         mMultiSimSettingControllerUT.notifyUserDataEnabled(1, false);
         processAllMessages();
-        verify(mDataEnabledSettingsMock2).setUserDataEnabled(false, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER,
+                false, PHONE_PACKAGE);
     }
 
     @Test
@@ -736,16 +752,16 @@
         // loaded on both subscriptions.
         mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
         processAllMessages();
-        verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
         processAllMessages();
-        verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
         mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
         processAllMessages();
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         // Switch from sub 2 to sub 3 in phone[1].
         clearInvocations(mSubControllerMock);
@@ -827,8 +843,8 @@
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         processAllMessages();
         // Nothing should happen as carrier config is not ready for sub 2.
-        verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2, never()).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
         // Still notify carrier config without specifying subId2, but this time subController
         // and CarrierConfigManager have subId 2 active and ready.
@@ -840,8 +856,8 @@
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         processAllMessages();
         // This time user data should be disabled on phone1.
-        verify(mDataEnabledSettingsMock2).setDataEnabled(
-                TelephonyManager.DATA_ENABLED_REASON_USER, false);
+        verify(mDataSettingsManagerMock2).setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
     }
 
     @Test
@@ -927,7 +943,7 @@
     public void onSubscriptionGroupChanged_allActiveSubArePartOfGroup() throws Exception {
         doReturn(3).when(mSubControllerMock).getDefaultDataSubId();
         // Create subscription grouping of subs 1 and 2.
-        replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
+        replaceInstance(SubscriptionInfo.class, "mGroupUuid", mSubInfo1, mGroupUuid1);
         doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(1);
         doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(2);
         GlobalSettingsHelper.setBoolean(mContext, Settings.Global.MOBILE_DATA, 1, true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 0230645..91d936d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.content.Intent;
@@ -34,18 +35,16 @@
 import android.os.PowerManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import com.android.internal.telephony.dataconnection.DataConnection;
+import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
 
@@ -53,6 +52,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -61,28 +61,9 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class NetworkTypeControllerTest extends TelephonyTest {
-    // private constants copied over from NetworkTypeController
-    private static final int EVENT_DATA_RAT_CHANGED = 2;
-    private static final int EVENT_NR_STATE_CHANGED = 3;
-    private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
-    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
-    private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
-    private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
-    private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
-    private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
-    private static final int EVENT_INITIALIZE = 12;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
-    private static final int EVENT_PCO_DATA_CHANGED = 14;
-
     private NetworkTypeController mNetworkTypeController;
     private PersistableBundle mBundle;
 
-    // Mocked classes
-    DataConnection mDataConnection;
-    ApnSetting mApnSetting;
-
     private IState getCurrentState() throws Exception {
         Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
         method.setAccessible(true);
@@ -106,8 +87,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mDataConnection = mock(DataConnection.class);
-        mApnSetting = mock(ApnSetting.class);
         mBundle = mContextFixture.getCarrierConfigBundle();
         mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:5G_Plus,connected:5G,not_restricted_rrc_idle:5G,"
@@ -118,7 +97,7 @@
         doReturn(RadioAccessFamily.getRafFromNetworkType(
                 TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA)).when(
                 mPhone).getCachedAllowedNetworkTypesBitmask();
-        doReturn(false).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+        doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
         doReturn(new int[] {0}).when(mServiceState).getCellBandwidths();
         mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
@@ -264,8 +243,7 @@
     public void testTransitionToCurrentStateLegacy() throws Exception {
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_HSPAP).when(mServiceState).getDataNetworkType();
-
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("legacy", getCurrentState().getName());
     }
@@ -275,8 +253,7 @@
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
-
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("restricted", getCurrentState().getName());
     }
@@ -286,9 +263,9 @@
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
     }
@@ -300,11 +277,12 @@
         mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
         processAllMessages();
         assertEquals("DefaultState", getCurrentState().getName());
+
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(false);
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
     }
@@ -319,14 +297,13 @@
         broadcastCarrierConfigs();
         processAllMessages();
         assertEquals("DefaultState", getCurrentState().getName());
+
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
-
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
     }
 
@@ -335,9 +312,9 @@
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
     }
@@ -351,11 +328,12 @@
         broadcastCarrierConfigs();
         processAllMessages();
         assertEquals("DefaultState", getCurrentState().getName());
+
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(true);
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
     }
@@ -371,14 +349,13 @@
         broadcastCarrierConfigs();
         processAllMessages();
         assertEquals("DefaultState", getCurrentState().getName());
+
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
-
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
     }
 
@@ -388,7 +365,7 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -400,7 +377,7 @@
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -423,7 +400,8 @@
         doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -446,7 +424,8 @@
         doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -457,10 +436,9 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -474,43 +452,13 @@
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
         mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
         broadcastCarrierConfigs();
-        int cid = 1;
-        byte[] contents = new byte[]{0};
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
-        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
-        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
-        broadcastCarrierConfigs();
 
-
-        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
-                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-        processAllMessages();
-        assertEquals("connected", getCurrentState().getName());
-    }
-
-    @Test
-    public void testTransitionToCurrentStateNrConnectedWithPcoLength4AndNoNrAdvancedCapable()
-            throws Exception {
-        assertEquals("DefaultState", getCurrentState().getName());
-        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
-        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
-        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
-        broadcastCarrierConfigs();
-        int cid = 1;
-        byte[] contents = new byte[]{31, 1, 84, 0};
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
-        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
-        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
-        broadcastCarrierConfigs();
-
-
-        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
-                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+                ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+        verify(mDataNetworkController).registerDataNetworkControllerCallback(
+                dataNetworkControllerCallbackCaptor.capture());
+        DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+        callback.onNrAdvancedCapableByPcoChanged(false);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -522,20 +470,15 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
-        broadcastCarrierConfigs();
-        int cid = 1;
-        byte[] contents = new byte[]{1};
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
-        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
-        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
         mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF00);
         broadcastCarrierConfigs();
 
-
-        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
-                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+                ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+        verify(mDataNetworkController).registerDataNetworkControllerCallback(
+                dataNetworkControllerCallbackCaptor.capture());
+        DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+        callback.onNrAdvancedCapableByPcoChanged(false);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -547,39 +490,15 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        int cid = 1;
-        byte[] contents = new byte[]{1};
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
-        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
-        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
         mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
-                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-        processAllMessages();
-        assertEquals("connected_mmwave", getCurrentState().getName());
-    }
-
-    @Test
-    public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapableAndPcoLength4()
-            throws Exception {
-        assertEquals("DefaultState", getCurrentState().getName());
-        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
-        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
-        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        int cid = 1;
-        byte[] contents = new byte[]{31, 1, 84, 1};
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
-        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
-        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
-        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
-        broadcastCarrierConfigs();
-
-        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
-                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+                ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+        verify(mDataNetworkController).registerDataNetworkControllerCallback(
+                dataNetworkControllerCallbackCaptor.capture());
+        DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+        callback.onNrAdvancedCapableByPcoChanged(true);
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -590,7 +509,7 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
 
-        mNetworkTypeController.sendMessage(EVENT_DATA_RAT_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -600,7 +519,7 @@
         testTransitionToCurrentStateNrConnected();
         doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
 
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("restricted", getCurrentState().getName());
     }
@@ -611,9 +530,8 @@
         testTransitionToCurrentStateNrConnectedMmwave();
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
 
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
-
         assertEquals("connected", getCurrentState().getName());
     }
 
@@ -623,9 +541,8 @@
         testTransitionToCurrentStateNrConnected();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
 
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
-
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
 
@@ -634,12 +551,11 @@
         testTransitionToCurrentStateNrConnectedMmwave();
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
-                new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
-        processAllMessages();
 
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
+                new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+        processAllMessages();
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
     }
 
@@ -654,9 +570,8 @@
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(true);
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         processAllMessages();
 
@@ -677,10 +592,9 @@
         testTransitionToCurrentStateNrConnectedMmwave();
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         processAllMessages();
 
@@ -697,7 +611,8 @@
 
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -715,9 +630,9 @@
         // Transition to LTE connected state
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -726,7 +641,8 @@
         // LTE -> LTE+
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -744,9 +660,9 @@
         // Transition to idle state
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -755,7 +671,8 @@
         // LTE -> LTE+
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -766,7 +683,7 @@
     public void testEventPhysicalLinkStatusChanged() throws Exception {
         testTransitionToCurrentStateLteConnected();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
 
         processAllMessages();
@@ -784,8 +701,8 @@
         testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
         setPhysicalLinkStatus(false);
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
-
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -803,7 +720,7 @@
         processAllMessages();
         testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+        mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
                 new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
 
         processAllMessages();
@@ -817,7 +734,7 @@
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
 
-        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED,
+        mNetworkTypeController.sendMessage(5 /* EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED */,
                 new AsyncResult(null, false, null));
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
@@ -833,7 +750,7 @@
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
         doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
 
-        mNetworkTypeController.sendMessage(EVENT_RADIO_OFF_OR_UNAVAILABLE);
+        mNetworkTypeController.sendMessage(9 /* EVENT_RADIO_OFF_OR_UNAVAILABLE */);
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
@@ -850,7 +767,7 @@
                 TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)).when(
                 mPhone).getCachedAllowedNetworkTypesBitmask();
 
-        mNetworkTypeController.sendMessage(EVENT_PREFERRED_NETWORK_MODE_CHANGED);
+        mNetworkTypeController.sendMessage(10 /* EVENT_PREFERRED_NETWORK_MODE_CHANGED */);
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
@@ -870,13 +787,13 @@
 
         // should trigger 10 second timer
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // timer expires
         moveTimeForward(10 * 1000);
@@ -885,7 +802,7 @@
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -901,7 +818,7 @@
                 new Handler(Looper.myLooper()));
         doReturn(pm).when(mContext).getSystemService(Context.POWER_SERVICE);
         doReturn(true).when(powerManager).isDeviceIdleMode();
-        mNetworkTypeController.sendMessage(17 /*EVENT_DEVICE_IDLE_MODE_CHANGED*/);
+        mNetworkTypeController.sendMessage(12 /* EVENT_DEVICE_IDLE_MODE_CHANGED */);
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
@@ -909,13 +826,13 @@
 
         // should not trigger timer
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -932,17 +849,17 @@
 
         // trigger 10 second timer after disconnecting from NR
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // reconnect to NR in the middle of the timer
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         // timer expires
         moveTimeForward(10 * 1000);
@@ -952,7 +869,7 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -975,14 +892,14 @@
         // trigger 10 second timer after disconnecting from NR, and then it does the timer reset
         // since the network mode without the NR capability.
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         // timer should be reset.
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1000,13 +917,13 @@
 
         // should trigger 10 second timer
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // timer expires
         moveTimeForward(10 * 1000);
@@ -1015,7 +932,7 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1033,17 +950,17 @@
 
         // trigger 10 second timer after disconnecting from NR
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // reconnect to NR in the middle of the timer
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         // timer expires
         moveTimeForward(10 * 1000);
@@ -1053,7 +970,7 @@
         assertEquals("connected_mmwave", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1072,13 +989,13 @@
 
         // should trigger 10 second primary timer
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // primary timer expires
         moveTimeForward(10 * 1000);
@@ -1088,7 +1005,7 @@
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // secondary timer expires
         moveTimeForward(30 * 1000);
@@ -1097,7 +1014,7 @@
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1116,13 +1033,13 @@
 
         // should trigger 10 second primary timer
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // primary timer expires
         moveTimeForward(10 * 1000);
@@ -1132,11 +1049,11 @@
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // reconnect to NR in the middle of the timer
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         // secondary timer expires
         moveTimeForward(30 * 1000);
@@ -1146,7 +1063,7 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1166,13 +1083,13 @@
 
         // should trigger 10 second primary timer
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // primary timer expires
         moveTimeForward(10 * 1000);
@@ -1182,7 +1099,7 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // secondary timer expires
         moveTimeForward(30 * 1000);
@@ -1191,7 +1108,7 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1211,13 +1128,13 @@
 
         // should trigger 10 second primary timer
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // primary timer expires
         moveTimeForward(10 * 1000);
@@ -1227,11 +1144,11 @@
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // reconnect to NR in the middle of the timer
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         // secondary timer expires
         moveTimeForward(30 * 1000);
@@ -1241,7 +1158,7 @@
         assertEquals("connected_mmwave", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     @Test
@@ -1261,13 +1178,13 @@
 
         // should trigger 10 second primary timer
         doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
-        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertTrue(mNetworkTypeController.is5GHysteresisActive());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
 
         // rat is UMTS, should stop timer
         NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
@@ -1276,13 +1193,13 @@
                 .build();
         doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(anyInt(), anyInt());
         doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
-        mNetworkTypeController.sendMessage(EVENT_DATA_RAT_CHANGED);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
 
         assertEquals("legacy", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
                 mNetworkTypeController.getOverrideNetworkType());
-        assertFalse(mNetworkTypeController.is5GHysteresisActive());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
     }
 
     private void setPhysicalLinkStatus(Boolean state) {
@@ -1312,7 +1229,7 @@
         mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -1327,7 +1244,7 @@
         mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -1342,7 +1259,7 @@
         mBundle.putBoolean(CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, false);
         broadcastCarrierConfigs();
 
-        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 5771326..81b9ff1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -138,6 +138,27 @@
 
     @Test
     @SmallTest
+    public void testConfigureAndGetMaxActiveVoiceSubscriptions() throws Exception {
+        init(2);
+        assertEquals(1, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
+
+        PhoneCapability dualActiveVoiceSubCapability = new PhoneCapability.Builder(
+                PhoneCapability.DEFAULT_DSDS_CAPABILITY)
+                .setMaxActiveVoiceSubscriptions(2)
+                .build();
+
+        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockRadioConfig).getPhoneCapability(captor.capture());
+        Message msg = captor.getValue();
+        AsyncResult.forMessage(msg, dualActiveVoiceSubCapability, null);
+        msg.sendToTarget();
+        processAllMessages();
+
+        assertEquals(2, mPcm.getStaticPhoneCapability().getMaxActiveVoiceSubscriptions());
+    }
+
+    @Test
+    @SmallTest
     public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception {
         init(1);
         assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index b7d2913..dd59e28 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -645,55 +645,27 @@
     @Test
     @Ignore
     public void testIsEmergencyNumber() {
-        // There are two parallel sets of tests here: one for the
-        // regular isEmergencyNumber() method, and the other for
-        // isPotentialEmergencyNumber().
-        //
         // (The difference is that isEmergencyNumber() will return true
         // only if the specified number exactly matches an actual
-        // emergency number, but isPotentialEmergencyNumber() will
-        // return true if the specified number simply starts with the
-        // same digits as any actual emergency number.)
+        // emergency number
 
         // Tests for isEmergencyNumber():
-        assertTrue(PhoneNumberUtils.isEmergencyNumber("911", "US"));
-        assertTrue(PhoneNumberUtils.isEmergencyNumber("112", "US"));
+        assertTrue(PhoneNumberUtils.isEmergencyNumber("911"));
+        assertTrue(PhoneNumberUtils.isEmergencyNumber("112"));
         // The next two numbers are not valid phone numbers in the US,
         // so do not count as emergency numbers (but they *are* "potential"
         // emergency numbers; see below.)
-        assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "US"));
-        assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345", "US"));
+        assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345"));
+        assertFalse(PhoneNumberUtils.isEmergencyNumber("11212345"));
         // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
         // in Singapore, as 911 is not an emergency number there.
-        assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234", "SG"));
+        assertFalse(PhoneNumberUtils.isEmergencyNumber("91121234"));
         // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
         // in Brazil, as 112 is not an emergency number there.
-        assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567", "BR"));
+        assertFalse(PhoneNumberUtils.isEmergencyNumber("1121234567"));
         // A valid local phone number from Brazil shouldn't be classified as an emergency number in
         // Brazil.
-        assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345", "BR"));
-
-        // Tests for isPotentialEmergencyNumber():
-        // These first two are obviously emergency numbers:
-        assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("911", "US"));
-        assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("112", "US"));
-        // The next two numbers are not valid phone numbers in the US, but can be used to trick the
-        // system to dial 911 and 112, which are emergency numbers in the US. For the purpose of
-        // addressing that, they are also classified as "potential" emergency numbers in the US.
-        assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US"));
-        assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US"));
-
-        // A valid mobile phone number from Singapore shouldn't be classified as an emergency number
-        // in Singapore, as 911 is not an emergency number there.
-        // This test fails on devices that have ecclist property preloaded with 911.
-        // assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
-
-        // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
-        // in Brazil, as 112 is not an emergency number there.
-        assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR"));
-        // A valid local phone number from Brazil shouldn't be classified as an emergency number in
-        // Brazil.
-        assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "BR"));
+        assertFalse(PhoneNumberUtils.isEmergencyNumber("91112345"));
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index 606bcec..5eaead5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -16,6 +16,7 @@
 package com.android.internal.telephony;
 
 import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -33,15 +34,27 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build;
+import android.os.RemoteException;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.telephony.uicc.IsimUiccRecords;
+import com.android.internal.telephony.uicc.SIMRecords;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccPort;
+import com.android.internal.telephony.uicc.UiccProfile;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 public class PhoneSubInfoControllerTest extends TelephonyTest {
     private static final String FEATURE_ID = "myfeatureId";
+    private static final String PSI_SMSC_TEL1 = "tel:+91123456789";
+    private static final String PSI_SMSC_SIP1 = "sip:+1234567890@msg.pc.t-mobile.com;user=phone";
+    private static final String PSI_SMSC_TEL2 = "tel:+91987654321";
+    private static final String PSI_SMSC_SIP2 = "sip:+19876543210@msg.pc.t-mobile.com;user=phone";
 
     private PhoneSubInfoController mPhoneSubInfoControllerUT;
     private AppOpsManager mAppOsMgr;
@@ -922,4 +935,234 @@
         assertEquals("VM_SIM_1", mPhoneSubInfoControllerUT
                 .getVoiceMailAlphaTagForSubscriber(1, TAG, FEATURE_ID));
     }
-}
+
+    private void setUpInitials() {
+        UiccPort uiccPort1 = Mockito.mock(UiccPort.class);
+        UiccProfile uiccProfile1 = Mockito.mock(UiccProfile.class);
+        UiccCardApplication uiccCardApplication1 = Mockito.mock(UiccCardApplication.class);
+        SIMRecords simRecords1 = Mockito.mock(SIMRecords.class);
+        IsimUiccRecords isimUiccRecords1 = Mockito.mock(IsimUiccRecords.class);
+
+        doReturn(uiccPort1).when(mPhone).getUiccPort();
+        doReturn(uiccProfile1).when(uiccPort1).getUiccProfile();
+        doReturn(uiccCardApplication1).when(uiccProfile1).getApplicationByType(anyInt());
+        doReturn(simRecords1).when(uiccCardApplication1).getIccRecords();
+        doReturn(isimUiccRecords1).when(uiccCardApplication1).getIccRecords();
+        doReturn(PSI_SMSC_TEL1).when(simRecords1).getSmscIdentity();
+        doReturn(PSI_SMSC_TEL1).when(isimUiccRecords1).getSmscIdentity();
+
+        doReturn(mUiccPort).when(mSecondPhone).getUiccPort();
+        doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+        doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+        doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
+        doReturn(PSI_SMSC_TEL2).when(mSimRecords).getSmscIdentity();
+        doReturn(PSI_SMSC_TEL2).when(mIsimUiccRecords).getSmscIdentity();
+    }
+
+    @Test
+    public void testGetSmscIdentityForTelUri() {
+        try {
+            setUpInitials();
+            assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 5));
+            assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 3));
+            assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+            assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testGetSmscIdentityForSipUri() {
+        try {
+            UiccPort uiccPort1 = Mockito.mock(UiccPort.class);
+            UiccProfile uiccProfile1 = Mockito.mock(UiccProfile.class);
+            UiccCardApplication uiccCardApplication1 = Mockito.mock(UiccCardApplication.class);
+            SIMRecords simRecords1 = Mockito.mock(SIMRecords.class);
+            IsimUiccRecords isimUiccRecords1 = Mockito.mock(IsimUiccRecords.class);
+
+            doReturn(uiccPort1).when(mPhone).getUiccPort();
+            doReturn(uiccProfile1).when(uiccPort1).getUiccProfile();
+            doReturn(uiccCardApplication1).when(uiccProfile1).getApplicationByType(anyInt());
+            doReturn(simRecords1).when(uiccCardApplication1).getIccRecords();
+            doReturn(isimUiccRecords1).when(uiccCardApplication1).getIccRecords();
+            doReturn(PSI_SMSC_SIP1).when(simRecords1).getSmscIdentity();
+            doReturn(PSI_SMSC_SIP1).when(isimUiccRecords1).getSmscIdentity();
+
+            doReturn(mUiccPort).when(mSecondPhone).getUiccPort();
+            doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+            doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+            doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+            doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
+            doReturn(PSI_SMSC_SIP2).when(mSimRecords).getSmscIdentity();
+            doReturn(PSI_SMSC_SIP2).when(mIsimUiccRecords).getSmscIdentity();
+
+            assertEquals(PSI_SMSC_SIP1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 5));
+            assertEquals(PSI_SMSC_SIP1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 3));
+            assertEquals(PSI_SMSC_SIP2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+            assertEquals(PSI_SMSC_SIP2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testGetSmscIdentityWithOutPermissions() {
+        setUpInitials();
+
+        //case 1: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        try {
+            mPhoneSubInfoControllerUT.getSmscIdentity(0, 5);
+            Assert.fail("expected Security Exception Thrown");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getSmscIdentity"));
+        }
+
+        try {
+            mPhoneSubInfoControllerUT.getSmscIdentity(1, 5);
+            Assert.fail("expected Security Exception Thrown");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getSmscIdentity"));
+        }
+
+        try {
+            mPhoneSubInfoControllerUT.getSmscIdentity(0, 3);
+            Assert.fail("expected Security Exception Thrown");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getSmscIdentity"));
+        }
+
+        try {
+            mPhoneSubInfoControllerUT.getSmscIdentity(1, 3);
+            Assert.fail("expected Security Exception Thrown");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getSmscIdentity"));
+        }
+
+        //case 2: no READ_PRIVILEGED_PHONE_STATE
+        mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
+                eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
+                nullable(String.class));
+
+        try {
+            assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 5));
+            assertEquals(PSI_SMSC_TEL1, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(0, 3));
+            assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+            assertEquals(PSI_SMSC_TEL2, mPhoneSubInfoControllerUT
+                    .getSmscIdentity(1, 5));
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testGetSimServiceTable() throws RemoteException {
+        String refSst = "1234567";
+        doReturn(mUiccPort).when(mPhone).getUiccPort();
+        doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+        doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+        doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+        String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+        assertEquals(refSst, resultSst);
+    }
+
+    @Test
+    public void testGetSimServiceTableEmpty() throws RemoteException {
+        String refSst = null;
+        doReturn(mUiccPort).when(mPhone).getUiccPort();
+        doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+        doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+        doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+        String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+        assertEquals(refSst, resultSst);
+    }
+
+    @Test
+    public void testGetSstWhenNoUiccPort() throws RemoteException {
+            String refSst = "1234567";
+            doReturn(null).when(mPhone).getUiccPort();
+            doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+            doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+            doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+            doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+            String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+            assertEquals(null, resultSst);
+    }
+
+    @Test
+    public void testGetSstWhenNoUiccProfile() throws RemoteException {
+        String refSst = "1234567";
+        doReturn(mUiccPort).when(mPhone).getUiccPort();
+        doReturn(null).when(mUiccPort).getUiccProfile();
+        doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+        doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+        String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+        assertEquals(null, resultSst);
+    }
+
+    @Test
+    public void testGetSstWhenNoUiccApplication() throws RemoteException {
+        String refSst = "1234567";
+        doReturn(mUiccPort).when(mPhone).getUiccPort();
+        doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+        doReturn(null).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+        doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+        String resultSst = mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+        assertEquals(null, resultSst);
+    }
+
+    @Test
+    public void testGetSimServiceTableWithOutPermissions() throws RemoteException {
+        String refSst = "1234567";
+        doReturn(mUiccPort).when(mPhone).getUiccPort();
+        doReturn(mUiccProfile).when(mUiccPort).getUiccProfile();
+        doReturn(mUiccCardApplicationIms).when(mUiccProfile).getApplicationByType(anyInt());
+        doReturn(mSimRecords).when(mUiccCardApplicationIms).getIccRecords();
+
+        doReturn(refSst).when(mSimRecords).getSimServiceTable();
+
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        try {
+            mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt());
+            Assert.fail("expected Security Exception Thrown");
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getSimServiceTable"));
+        }
+
+        mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+        assertEquals(refSst, mPhoneSubInfoControllerUT.getSimServiceTable(anyInt(), anyInt()));
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 311fe20..8054c20 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE;
@@ -183,8 +188,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -206,13 +213,13 @@
     private RadioNetworkProxy mNetworkProxy;
     private RadioSimProxy mSimProxy;
 
-    private HalVersion mRadioVersionV10 = new HalVersion(1, 0);
-    private HalVersion mRadioVersionV11 = new HalVersion(1, 1);
-    private HalVersion mRadioVersionV12 = new HalVersion(1, 2);
-    private HalVersion mRadioVersionV13 = new HalVersion(1, 3);
-    private HalVersion mRadioVersionV14 = new HalVersion(1, 4);
-    private HalVersion mRadioVersionV15 = new HalVersion(1, 5);
-    private HalVersion mRadioVersionV16 = new HalVersion(1, 6);
+    private Map<Integer, HalVersion> mHalVersionV10 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV11 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV12 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV13 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV14 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV15 = new HashMap<>();
+    private Map<Integer, HalVersion> mHalVersionV16 = new HashMap<>();
 
     private RIL mRILInstance;
     private RIL mRILUnderTest;
@@ -316,10 +323,10 @@
         doReturn(powerManager).when(context).getSystemService(Context.POWER_SERVICE);
         doReturn(new ApplicationInfo()).when(context).getApplicationInfo();
         SparseArray<RadioServiceProxy> proxies = new SparseArray<>();
-        proxies.put(RIL.RADIO_SERVICE, null);
-        proxies.put(RIL.DATA_SERVICE, mDataProxy);
-        proxies.put(RIL.NETWORK_SERVICE, mNetworkProxy);
-        proxies.put(RIL.SIM_SERVICE, mSimProxy);
+        proxies.put(HAL_SERVICE_RADIO, null);
+        proxies.put(HAL_SERVICE_DATA, mDataProxy);
+        proxies.put(HAL_SERVICE_NETWORK, mNetworkProxy);
+        proxies.put(HAL_SERVICE_SIM, mSimProxy);
         mRILInstance = new RIL(context,
                 RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE),
                 Phone.PREFERRED_CDMA_SUBSCRIPTION, 0, proxies);
@@ -335,7 +342,16 @@
         doReturn(false).when(mNetworkProxy).isEmpty();
         doReturn(false).when(mSimProxy).isEmpty();
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV10);
+            for (int service = RIL.MIN_SERVICE_IDX; service <= RIL.MAX_SERVICE_IDX; service++) {
+                mHalVersionV10.put(service, new HalVersion(1, 0));
+                mHalVersionV11.put(service, new HalVersion(1, 1));
+                mHalVersionV12.put(service, new HalVersion(1, 2));
+                mHalVersionV13.put(service, new HalVersion(1, 3));
+                mHalVersionV14.put(service, new HalVersion(1, 4));
+                mHalVersionV15.put(service, new HalVersion(1, 5));
+                mHalVersionV16.put(service, new HalVersion(1, 6));
+            }
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV10);
         } catch (Exception e) {
         }
     }
@@ -493,7 +509,7 @@
 
         // Make radio version 1.5 to support the operation.
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
 
@@ -531,7 +547,7 @@
 
         // Make radio version 1.5 to support the operation.
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
 
@@ -711,7 +727,7 @@
     public void testStartNetworkScanWithUnsupportedResponse() throws Exception {
         // Use Radio HAL v1.5
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
         NetworkScanRequest nsr = getNetworkScanRequestForTesting();
@@ -738,7 +754,7 @@
     public void testGetVoiceRegistrationStateWithUnsupportedResponse() throws Exception {
         // Use Radio HAL v1.5
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
         mRILUnderTest.getVoiceRegistrationState(obtainMessage());
@@ -773,7 +789,7 @@
     public void testGetDataRegistrationStateWithUnsupportedResponse() throws Exception {
         // Use Radio HAL v1.5
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
 
@@ -818,7 +834,7 @@
 
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
 
@@ -861,7 +877,7 @@
     public void testSendSMS_1_6() throws Exception {
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
         String smscPdu = "smscPdu";
@@ -893,7 +909,7 @@
     public void testSendSMSExpectMore_1_6() throws Exception {
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
         String smscPdu = "smscPdu";
@@ -912,7 +928,7 @@
     public void testSendCdmaSMS_1_6() throws Exception {
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
         byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
@@ -928,7 +944,7 @@
     public void testSendCdmaSMSExpectMore_1_6() throws Exception {
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
         byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
@@ -1475,7 +1491,7 @@
 
         // Make radio version 1.5 to support the operation.
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
         mRILUnderTest.getBarringInfo(obtainMessage());
@@ -2838,7 +2854,7 @@
 
         // Make radio version 1.5 to support the operation.
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
         mRILUnderTest.enableUiccApplications(false, obtainMessage());
@@ -2855,7 +2871,7 @@
 
         // Make radio version 1.5 to support the operation.
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV15);
         } catch (Exception e) {
         }
         mRILUnderTest.areUiccApplicationsEnabled(obtainMessage());
@@ -2902,7 +2918,7 @@
     public void testGetSlicingConfig() throws Exception {
         // Use Radio HAL v1.6
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+            replaceInstance(RIL.class, "mHalVersion", mRILUnderTest, mHalVersionV16);
         } catch (Exception e) {
         }
         mRILUnderTest.getSlicingConfig(obtainMessage());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
deleted file mode 100644
index 80cd9f1..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.test.suitebuilder.annotation.MediumTest;
-import com.android.internal.telephony.TestPhoneNotifier;
-import com.android.internal.telephony.gsm.SmsMessage;
-import com.android.internal.telephony.test.SimulatedCommands;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-import com.android.internal.telephony.uicc.IccUtils;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-
-import java.util.Iterator;
-
-/**
- * {@hide}
- */
-public class SMSDispatcherTest extends AndroidTestCase {
-    @MediumTest
-    public void testCMT1() throws Exception {
-        SmsMessage sms;
-        SmsHeader header;
-
-        String[] lines = new String[2];
-
-        lines[0] = "+CMT: ,158";
-        lines[1] = "07914140279510F6440A8111110301003BF56080426101748A8C0B05040B"
-                 + "8423F000035502010106276170706C69636174696F6E2F766E642E776170"
-                 + "2E6D6D732D6D65737361676500AF848D0185B4848C8298524F347839776F"
-                 + "7547514D4141424C3641414141536741415A4B554141414141008D908918"
-                 + "802B31363530323438363137392F545950453D504C4D4E008A808E028000"
-                 + "88058103093A8083687474703A2F2F36";
-
-        sms = SmsMessage.newFromCMT(lines);
-        header = sms.getUserDataHeader();
-        assertNotNull(header);
-        assertNotNull(sms.getUserData());
-        assertNotNull(header.concatRef);
-        assertEquals(header.concatRef.refNumber, 85);
-        assertEquals(header.concatRef.msgCount, 2);
-        assertEquals(header.concatRef.seqNumber, 1);
-        assertEquals(header.concatRef.isEightBits, true);
-        assertNotNull(header.portAddrs);
-        assertEquals(header.portAddrs.destPort, 2948);
-        assertEquals(header.portAddrs.origPort, 9200);
-        assertEquals(header.portAddrs.areEightBits, false);
-    }
-
-    @MediumTest
-    public void testCMT2() throws Exception {
-        SmsMessage sms;
-        SmsHeader header;
-
-        String[] lines = new String[2];
-
-        lines[0] = "+CMT: ,77";
-        lines[1] = "07914140279510F6440A8111110301003BF56080426101848A3B0B05040B8423F"
-                 + "00003550202362E3130322E3137312E3135302F524F347839776F7547514D4141"
-                 + "424C3641414141536741415A4B55414141414100";
-
-        sms = SmsMessage.newFromCMT(lines);
-        header = sms.getUserDataHeader();
-        assertNotNull(header);
-        assertNotNull(sms.getUserData());
-        assertNotNull(header.concatRef);
-        assertEquals(header.concatRef.refNumber, 85);
-        assertEquals(header.concatRef.msgCount, 2);
-        assertEquals(header.concatRef.seqNumber, 2);
-        assertEquals(header.concatRef.isEightBits, true);
-        assertNotNull(header.portAddrs);
-        assertEquals(header.portAddrs.destPort, 2948);
-        assertEquals(header.portAddrs.origPort, 9200);
-        assertEquals(header.portAddrs.areEightBits, false);
-    }
-
-    @MediumTest
-    public void testEfRecord() throws Exception {
-        SmsMessage sms;
-
-        String s = "03029111000c9194981492631000f269206190022000a053e4534a05358bd3"
-                 + "69f05804259da0219418a40641536a110a0aea408080604028180e888462c1"
-                 + "50341c0f484432a1542c174c46b3e1743c9f9068442a994ea8946ac56ab95e"
-                 + "b0986c46abd96eb89c6ec7ebf97ec0a070482c1a8fc8a472c96c3a9fd0a874"
-                 + "4aad5aafd8ac76cbed7abfe0b0784c2e9bcfe8b47acd6ebbdff0b87c4eafdb"
-                 + "eff8bc7ecfeffbffffffffffffffffffffffffffff";
-       byte[] data = IccUtils.hexStringToBytes(s);
-
-       sms = SmsMessage.createFromEfRecord(1, data);
-       assertNotNull(sms.getMessageBody());
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 0cad6ee..51f501a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -235,7 +235,6 @@
         doReturn(mIwlanNetworkServiceStub).when(mIwlanNetworkServiceStub).asBinder();
         addNetworkService();
 
-        doReturn(true).when(mDcTracker).areAllDataDisconnected();
         doReturn(true).when(mDataNetworkController).areAllDataDisconnected();
 
         doReturn(new ServiceState()).when(mPhone).getServiceState();
@@ -261,7 +260,6 @@
 
         int dds = SubscriptionManager.getDefaultDataSubscriptionId();
         doReturn(dds).when(mPhone).getSubId();
-        doReturn(true).when(mPhone).areAllDataDisconnected();
 
         doReturn(true).when(mPackageManager)
                 .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
@@ -391,7 +389,6 @@
         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
         doReturn(dataNetworkController_phone2).when(phone2).getDataNetworkController();
         doReturn(mSST).when(phone2).getServiceStateTracker();
-        doReturn(true).when(phone2).isUsingNewDataStack();
         doReturn(false).when(mDataNetworkController).areAllDataDisconnected();
         doReturn(false).when(dataNetworkController_phone2).areAllDataDisconnected();
         doReturn(1).when(mPhone).getSubId();
@@ -467,30 +464,37 @@
     @Test
     @MediumTest
     public void testSetRadioPowerForReason() {
+        testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL);
+        testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE);
+        testSetRadioPowerForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER);
+    }
+
+    private void testSetRadioPowerForReason(int reason) {
         // Radio does not turn on if off for other reason and not emergency call.
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
         assertTrue(sst.getRadioPowerOffReasons().isEmpty());
-        sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
-        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        sst.setRadioPowerForReason(false, false, false, false, reason);
+        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
         assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
-        sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_USER);
-        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        sst.setRadioPowerForReason(true, false, false, false,
+                TelephonyManager.RADIO_POWER_REASON_USER);
+        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
         assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
 
         // Radio power state reason is removed and radio turns on if turned on for same reason it
         // had been turned off for.
-        sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
+        sst.setRadioPowerForReason(true, false, false, false, reason);
         assertTrue(sst.getRadioPowerOffReasons().isEmpty());
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
 
         // Turn radio off, then successfully turn radio on for emergency call.
-        sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
-        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        sst.setRadioPowerForReason(false, false, false, false, reason);
+        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
         assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
@@ -502,33 +506,83 @@
 
     @Test
     @MediumTest
-    public void testSetRadioPowerFromCarrier() {
+    public void testSetRadioPowerForMultipleReasons() {
+        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
+
+        // Turn off radio
+        turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_USER, 1);
+        turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL, 2);
+        turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER, 3);
+        turnRadioOffForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE, 4);
+
+        // Turn on radio
+        turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_NEARBY_DEVICE, 3,
+                TelephonyManager.RADIO_POWER_OFF);
+        turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_THERMAL, 2,
+                TelephonyManager.RADIO_POWER_OFF);
+        turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_CARRIER, 1,
+                TelephonyManager.RADIO_POWER_OFF);
+        turnRadioOnForReason(TelephonyManager.RADIO_POWER_REASON_USER, 0,
+                TelephonyManager.RADIO_POWER_ON);
+    }
+
+    private void turnRadioOffForReason(int reason, int powerOffReasonSize) {
+        sst.setRadioPowerForReason(false, false, false, false, reason);
+        assertTrue(sst.getRadioPowerOffReasons().contains(reason));
+        assertTrue(sst.getRadioPowerOffReasons().size() == powerOffReasonSize);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+    }
+
+    private void turnRadioOnForReason(int reason, int powerOffReasonSize,
+            int expectedRadioPowerState) {
+        sst.setRadioPowerForReason(true, false, false, false, reason);
+        assertFalse(sst.getRadioPowerOffReasons().contains(reason));
+        assertTrue(sst.getRadioPowerOffReasons().size() == powerOffReasonSize);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertTrue(mSimulatedCommands.getRadioState() == expectedRadioPowerState);
+    }
+
+    @Test
+    @MediumTest
+    public void testSetRadioPowerForReasonCarrier() {
         // Carrier disable radio power
-        sst.setRadioPowerFromCarrier(false);
+        sst.setRadioPowerForReason(false, false, false, false,
+                TelephonyManager.RADIO_POWER_REASON_CARRIER);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertFalse(mSimulatedCommands.getRadioState()
                 == TelephonyManager.RADIO_POWER_ON);
-        assertTrue(sst.getDesiredPowerState());
+        assertFalse(sst.getDesiredPowerState());
         assertFalse(sst.getPowerStateFromCarrier());
+        assertTrue(sst.getRadioPowerOffReasons().contains(
+                TelephonyManager.RADIO_POWER_REASON_CARRIER));
+        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
 
         // User toggle radio power will not overrides carrier settings
         sst.setRadioPower(true);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertFalse(mSimulatedCommands.getRadioState()
                 == TelephonyManager.RADIO_POWER_ON);
-        assertTrue(sst.getDesiredPowerState());
+        assertFalse(sst.getDesiredPowerState());
         assertFalse(sst.getPowerStateFromCarrier());
+        assertTrue(sst.getRadioPowerOffReasons().contains(
+                TelephonyManager.RADIO_POWER_REASON_CARRIER));
+        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
 
         // Carrier re-enable radio power
-        sst.setRadioPowerFromCarrier(true);
+        sst.setRadioPowerForReason(true, false, false, false,
+                TelephonyManager.RADIO_POWER_REASON_CARRIER);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
         assertTrue(sst.getDesiredPowerState());
         assertTrue(sst.getPowerStateFromCarrier());
+        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
 
         // User toggle radio power off (airplane mode) and set carrier on
         sst.setRadioPower(false);
-        sst.setRadioPowerFromCarrier(true);
+        sst.setRadioPowerForReason(true, false, false, false,
+                TelephonyManager.RADIO_POWER_REASON_CARRIER);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertFalse(mSimulatedCommands.getRadioState()
                 == TelephonyManager.RADIO_POWER_ON);
@@ -1771,29 +1825,6 @@
         assertEquals(TelephonyManager.RADIO_POWER_UNAVAILABLE, mSimulatedCommands.getRadioState());
     }
 
-    @Test
-    @SmallTest
-    public void testImsRegisteredDelayShutDown() throws Exception {
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mContextFixture.putIntResource(
-                com.android.internal.R.integer.config_delay_for_ims_dereg_millis, 1000 /*ms*/);
-        sst.setImsRegistrationState(true);
-        mSimulatedCommands.setRadioPowerFailResponse(false);
-        sst.setRadioPower(true);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
-        // Turn off the radio and ensure radio power is still on
-        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-        sst.setRadioPower(false);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-
-        // Now set IMS reg state to false and ensure we see the modem move to power off.
-        sst.setImsRegistrationState(false);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-        assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
-    }
 
     @Test
     @SmallTest
@@ -1814,33 +1845,6 @@
 
     @Test
     @SmallTest
-    public void testImsRegisteredDelayShutDownTimeout() throws Exception {
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        doReturn(true).when(mPhone).isPhoneTypeGsm();
-        mContextFixture.putIntResource(
-                com.android.internal.R.integer.config_delay_for_ims_dereg_millis, 1000 /*ms*/);
-        sst.setImsRegistrationState(true);
-        mSimulatedCommands.setRadioPowerFailResponse(false);
-        sst.setRadioPower(true);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
-        // Turn off the radio and ensure radio power is still on
-        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-        sst.setRadioPower(false);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
-
-        // Ensure that if we never turn deregister for IMS, we still eventually see radio state
-        // move to off.
-        // Timeout for IMS reg + some extra time to remove race conditions
-        waitForDelayedHandlerAction(mSSTTestHandler.getThreadHandler(),
-                sst.getRadioPowerOffDelayTimeoutForImsRegistration() + 1000, 1000);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-        assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
-    }
-
-    @Test
-    @SmallTest
     public void testImsRegisteredAPMOnOffToggle() throws Exception {
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mContextFixture.putIntResource(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
index 50a1b96..8b4a2fa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
@@ -19,6 +19,7 @@
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP;
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -741,7 +742,7 @@
             }
         }
         // Only check on RADIO hal 1.5 and above to make it less flaky
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+        if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             assertThat(expectedNonEmptyThreshold).isEqualTo(actualNonEmptyThreshold);
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
index b282c55..5f39c09 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
@@ -42,69 +42,94 @@
 public class SignalThresholdInfoTest extends TestCase {
     private static final int HYSTERESIS_DB = 2;
     private static final int HYSTERESIS_MS = 30;
-    private static final int[] SSRSRP_THRESHOLDS = new int[]{-120, -100, -80, -60};
+    private static final int[] SSRSRP_THRESHOLDS = new int[] {-120, -100, -80, -60};
 
     // Map of SignalMeasurementType to invalid thresholds edge values.
     // Each invalid value will be constructed with a thresholds array to test separately.
-    private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP = Map.of(
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
-            List.of(SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
-            List.of(SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
-            List.of(SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
-            List.of(SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
-            List.of(SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
-            List.of(SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
-            List.of(SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
-            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
-            List.of(SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
-                    SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1)
-    );
+    private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP =
+            Map.of(
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1),
+                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO,
+                    List.of(
+                            SignalThresholdInfo.SIGNAL_ECNO_MIN_VALUE - 1,
+                            SignalThresholdInfo.SIGNAL_ECNO_MAX_VALUE + 1));
 
     // Map of RAN to allowed SignalMeasurementType set.
     // RAN/TYPE pair will be used to verify the validation of the combo
-    private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP = Map.of(
-            AccessNetworkConstants.AccessNetworkType.GERAN,
-            Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
-            AccessNetworkConstants.AccessNetworkType.CDMA2000,
-            Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
-            AccessNetworkConstants.AccessNetworkType.UTRAN,
-            Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP),
-            AccessNetworkConstants.AccessNetworkType.EUTRAN,
-            Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
-                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
-                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
-            AccessNetworkConstants.AccessNetworkType.NGRAN,
-            Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
-                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
-                    SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
-    );
+    private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP =
+            Map.of(
+                    AccessNetworkConstants.AccessNetworkType.GERAN,
+                    Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+                    AccessNetworkConstants.AccessNetworkType.CDMA2000,
+                    Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+                    AccessNetworkConstants.AccessNetworkType.UTRAN,
+                    Set.of(
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO),
+                    AccessNetworkConstants.AccessNetworkType.EUTRAN,
+                    Set.of(
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
+                    AccessNetworkConstants.AccessNetworkType.NGRAN,
+                    Set.of(
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+                            SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR));
 
     // Deliberately picking up the max/min value in each range to test the edge cases
-    private final int[] mRssiThresholds = new int[]{-113, -103, -97, -51};
-    private final int[] mRscpThresholds = new int[]{-120, -105, -95, -25};
-    private final int[] mRsrpThresholds = new int[]{-140, -118, -108, -44};
-    private final int[] mRsrqThresholds = new int[]{-34, -17, -14, 3};
-    private final int[] mRssnrThresholds = new int[]{-20, 10, 20, 30};
-    private final int[] mSsrsrpThresholds = new int[]{-140, -118, -98, -44};
-    private final int[] mSsrsrqThresholds = new int[]{-43, -17, -14, 20};
-    private final int[] mSssinrThresholds = new int[]{-23, -16, -10, 40};
+    private final int[] mRssiThresholds = new int[] {-113, -103, -97, -51};
+    private final int[] mRscpThresholds = new int[] {-120, -105, -95, -25};
+    private final int[] mRsrpThresholds = new int[] {-140, -118, -108, -44};
+    private final int[] mRsrqThresholds = new int[] {-34, -17, -14, 3};
+    private final int[] mRssnrThresholds = new int[] {-20, 10, 20, 30};
+    private final int[] mSsrsrpThresholds = new int[] {-140, -118, -98, -44};
+    private final int[] mSsrsrqThresholds = new int[] {-43, -17, -14, 20};
+    private final int[] mSssinrThresholds = new int[] {-23, -16, -10, 40};
+    private final int[] mEcnoThresholds = new int[] {-24, -16, -8, 1};
 
-    private final int[][] mThresholds = {mRssiThresholds, mRscpThresholds, mRsrpThresholds,
-            mRsrqThresholds, mRssnrThresholds, mSsrsrpThresholds, mSsrsrqThresholds,
-            mSssinrThresholds};
+    private final int[][] mThresholds = {
+        mRssiThresholds,
+        mRscpThresholds,
+        mRsrpThresholds,
+        mRsrqThresholds,
+        mRssnrThresholds,
+        mSsrsrpThresholds,
+        mSsrsrqThresholds,
+        mSssinrThresholds,
+        mEcnoThresholds
+    };
 
     @Test
     @SmallTest
@@ -120,12 +145,14 @@
                         .setIsEnabled(false)
                         .build();
 
-        assertEquals(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+        assertEquals(
+                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
                 signalThresholdInfo.getSignalMeasurementType());
         assertEquals(HYSTERESIS_MS, signalThresholdInfo.getHysteresisMs());
         assertEquals(HYSTERESIS_DB, signalThresholdInfo.getHysteresisDb());
-        assertEquals(Arrays.toString(SSRSRP_THRESHOLDS), Arrays.toString(
-                signalThresholdInfo.getThresholds()));
+        assertEquals(
+                Arrays.toString(SSRSRP_THRESHOLDS),
+                Arrays.toString(signalThresholdInfo.getThresholds()));
         assertFalse(signalThresholdInfo.isEnabled());
     }
 
@@ -160,63 +187,90 @@
     @SmallTest
     public void testGetSignalThresholdInfo() {
         ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
-                .setHysteresisMs(0)
-                .setHysteresisDb(0)
-                .setThresholds(new int[]{}, true /*isSystem*/)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
-                .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRssiThresholds)
-                .setIsEnabled(false)
-                .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+                        .setHysteresisMs(0)
+                        .setHysteresisDb(0)
+                        .setThresholds(new int[] {}, true /*isSystem*/)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssiThresholds)
+                        .setIsEnabled(false)
+                        .build());
 
-        assertThat(stList.get(0).getThresholds()).isEqualTo(new int[]{});
-        assertThat(stList.get(1).getSignalMeasurementType()).isEqualTo(
-                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
+        assertThat(stList.get(0).getThresholds()).isEqualTo(new int[] {});
+        assertThat(stList.get(1).getSignalMeasurementType())
+                .isEqualTo(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
         assertThat(stList.get(1).getThresholds()).isEqualTo(mRssiThresholds);
     }
 
     @Test
     @SmallTest
     public void testEqualsSignalThresholdInfo() {
-        final int[] dummyThresholds = new int[]{-100, -90, -70, -60};
-        final int[] dummyThreholdsDisordered = new int[]{-60, -90, -100, -70};
-        SignalThresholdInfo st1 = new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(1).setSignalMeasurementType(1)
-                .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRssiThresholds).setIsEnabled(false)
-                .build();
-        SignalThresholdInfo st2 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(2)
-                .setSignalMeasurementType(2).setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
-                .build();
-        SignalThresholdInfo st3 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
-                .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThresholds).setIsEnabled(false)
-                .build();
-        SignalThresholdInfo st4 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
-                .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
-                .build();
-        SignalThresholdInfo st5 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
-                .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThreholdsDisordered)
-                .setIsEnabled(false).build();
+        final int[] dummyThresholds = new int[] {-100, -90, -70, -60};
+        final int[] dummyThreholdsDisordered = new int[] {-60, -90, -100, -70};
+        SignalThresholdInfo st1 =
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(1)
+                        .setSignalMeasurementType(1)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssiThresholds)
+                        .setIsEnabled(false)
+                        .build();
+        SignalThresholdInfo st2 =
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(2)
+                        .setSignalMeasurementType(2)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssiThresholds)
+                        .setIsEnabled(false)
+                        .build();
+        SignalThresholdInfo st3 =
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(1)
+                        .setSignalMeasurementType(1)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(dummyThresholds)
+                        .setIsEnabled(false)
+                        .build();
+        SignalThresholdInfo st4 =
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(1)
+                        .setSignalMeasurementType(1)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssiThresholds)
+                        .setIsEnabled(false)
+                        .build();
+        SignalThresholdInfo st5 =
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(1)
+                        .setSignalMeasurementType(1)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(dummyThreholdsDisordered)
+                        .setIsEnabled(false)
+                        .build();
 
-        //Return true if all SignalThresholdInfo values match.
+        // Return true if all SignalThresholdInfo values match.
         assertTrue(st1.equals(st1));
         assertFalse(st1.equals(st2));
         assertFalse(st1.equals(st3));
         assertTrue(st1.equals(st4));
-        //Threshold values ordering doesn't matter
+        // Threshold values ordering doesn't matter
         assertTrue(st3.equals(st5));
-        //Return false if the object of argument is other than SignalThresholdInfo.
+        // Return false if the object of argument is other than SignalThresholdInfo.
         assertFalse(st1.equals(new String("test")));
     }
 
@@ -238,33 +292,40 @@
     @SmallTest
     public void testBuilderWithInvalidParameter() {
         // Invalid signal measurement type
-        int[] invalidSignalMeasurementTypes = new int[]{-1, 0, 9};
+        int[] invalidSignalMeasurementTypes = new int[] {-1, 0, 9};
         for (int signalMeasurementType : invalidSignalMeasurementTypes) {
             buildWithInvalidParameterThrowException(
-                    AccessNetworkConstants.AccessNetworkType.GERAN, signalMeasurementType,
-                    new int[]{-1});
+                    AccessNetworkConstants.AccessNetworkType.GERAN,
+                    signalMeasurementType,
+                    new int[] {-1});
         }
 
         // Null thresholds array
-        buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
-                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, null);
+        buildWithInvalidParameterThrowException(
+                AccessNetworkConstants.AccessNetworkType.GERAN,
+                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+                null);
 
         // Empty thresholds
-        buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
-                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, new int[]{});
-
+        buildWithInvalidParameterThrowException(
+                AccessNetworkConstants.AccessNetworkType.GERAN,
+                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+                new int[] {});
 
         // Too long thresholds array
-        buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+        buildWithInvalidParameterThrowException(
+                AccessNetworkConstants.AccessNetworkType.GERAN,
                 SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
-                new int[]{-100, -90, -70, -60, -58});
+                new int[] {-100, -90, -70, -60, -58});
 
         // Thresholds value out of range
         for (int signalMeasurementType : INVALID_THRESHOLDS_MAP.keySet()) {
             List<Integer> invalidThresholds = INVALID_THRESHOLDS_MAP.get(signalMeasurementType);
             for (int threshold : invalidThresholds) {
-                buildWithInvalidParameterThrowException(getValidRan(signalMeasurementType),
-                        signalMeasurementType, new int[]{threshold});
+                buildWithInvalidParameterThrowException(
+                        getValidRan(signalMeasurementType),
+                        signalMeasurementType,
+                        new int[] {threshold});
             }
         }
 
@@ -272,16 +333,17 @@
         for (int ran : VALID_RAN_TO_MEASUREMENT_TYPE_MAP.keySet()) {
             Set validTypes = VALID_RAN_TO_MEASUREMENT_TYPE_MAP.get(ran);
             for (int type = SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
-                    type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR; type++) {
+                    type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO;
+                    type++) {
                 if (!validTypes.contains(type)) {
-                    buildWithInvalidParameterThrowException(ran, type, new int[]{-1});
+                    buildWithInvalidParameterThrowException(ran, type, new int[] {-1});
                 }
             }
         }
     }
 
-    private void buildWithInvalidParameterThrowException(int ran, int signalMeasurementType,
-            int[] thresholds) {
+    private void buildWithInvalidParameterThrowException(
+            int ran, int signalMeasurementType, int[] thresholds) {
         try {
             new SignalThresholdInfo.Builder()
                     .setRadioAccessNetworkType(ran)
@@ -296,68 +358,90 @@
     private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithAllFields() {
         ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
 
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
-                .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRssiThresholds).setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRscpThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRsrpThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRsrqThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mRssnrThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mSsrsrpThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mSsrsrqThresholds)
-                .setIsEnabled(false)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
-                .setHysteresisMs(HYSTERESIS_MS)
-                .setHysteresisDb(HYSTERESIS_DB)
-                .setThresholds(mSssinrThresholds)
-                .setIsEnabled(false)
-                .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssiThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRscpThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRsrpThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRsrqThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mRssnrThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mSsrsrpThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mSsrsrqThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mSssinrThresholds)
+                        .setIsEnabled(false)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO)
+                        .setHysteresisMs(HYSTERESIS_MS)
+                        .setHysteresisDb(HYSTERESIS_DB)
+                        .setThresholds(mEcnoThresholds)
+                        .setIsEnabled(false)
+                        .build());
 
         return stList;
     }
@@ -365,46 +449,63 @@
     private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithPublicFields() {
         ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
 
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
-                .setThresholds(mRssiThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
-                .setThresholds(mRscpThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
-                .setThresholds(mRsrpThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
-                .setThresholds(mRsrqThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
-                .setThresholds(mRssnrThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
-                .setThresholds(mSsrsrpThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
-                .setThresholds(mSsrsrqThresholds)
-                .build());
-        stList.add(new SignalThresholdInfo.Builder()
-                .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
-                .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
-                .setThresholds(mSssinrThresholds)
-                .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+                        .setThresholds(mRssiThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+                        .setThresholds(mRscpThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+                        .setThresholds(mRsrpThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+                        .setThresholds(mRsrqThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+                        .setThresholds(mRssnrThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+                        .setThresholds(mSsrsrpThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+                        .setThresholds(mSsrsrqThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+                        .setSignalMeasurementType(
+                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+                        .setThresholds(mSssinrThresholds)
+                        .build());
+        stList.add(
+                new SignalThresholdInfo.Builder()
+                        .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+                        .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO)
+                        .setThresholds(mEcnoThresholds)
+                        .build());
 
         return stList;
     }
@@ -418,6 +519,7 @@
             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
                 return AccessNetworkConstants.AccessNetworkType.GERAN;
             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
+            case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_ECNO:
                 return AccessNetworkConstants.AccessNetworkType.UTRAN;
             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
             case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 6a075da..e9b644f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -66,6 +66,7 @@
 import com.android.internal.telephony.RILUtils;
 import com.android.internal.telephony.RadioCapability;
 import com.android.internal.telephony.SmsResponse;
+import com.android.internal.telephony.SrvccConnection;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
@@ -186,6 +187,9 @@
     public boolean mSetRadioPowerForEmergencyCall;
     public boolean mSetRadioPowerAsSelectedPhoneForEmergencyCall;
 
+    public boolean mCallWaitActivated = false;
+    private SrvccConnection[] mSrvccConnections;
+
     // mode for Icc Sim Authentication
     private int mAuthenticationMode;
     //***** Constructor
@@ -1432,6 +1436,14 @@
 
     @Override
     public void queryCallWaiting(int serviceClass, Message response) {
+        if (response != null && serviceClass == SERVICE_CLASS_NONE) {
+            int[] r = new int[2];
+            r[0] = (mCallWaitActivated ? 1 : 0);
+            r[1] = (mCallWaitActivated ? SERVICE_CLASS_VOICE : SERVICE_CLASS_NONE);
+            resultSuccess(response, r);
+            return;
+        }
+
         unimplemented(response);
     }
 
@@ -1440,11 +1452,15 @@
      * @param serviceClass is a sum of SERVICE_CLASS_*
      * @param response is callback message
      */
-
     @Override
     public void setCallWaiting(boolean enable, int serviceClass,
             Message response) {
-        unimplemented(response);
+        if ((serviceClass & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE) {
+            mCallWaitActivated = enable;
+        }
+        if (response != null) {
+            resultSuccess(response, null);
+        }
     }
 
     /**
@@ -2558,4 +2574,13 @@
         PcoData response = new PcoData(cid, bearerProto, pcoId, contents);
         mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
+
+    @Override
+    public void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {
+        mSrvccConnections = srvccConnections;
+    }
+
+    public SrvccConnection[] getSrvccConnections() {
+        return mSrvccConnections;
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
new file mode 100644
index 0000000..63f7d00
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
+import com.android.internal.telephony.uicc.IccConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SmsControllerTest extends TelephonyTest {
+
+    // Mocked classes
+    private AdnRecordCache mAdnRecordCache;
+
+    // SmsController under test
+    private SmsController mSmsControllerUT;
+    private final String smscAddrStr = "+1206313004";
+    private String mCallingPackage;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mAdnRecordCache = Mockito.mock(AdnRecordCache.class);
+        mSmsControllerUT = new SmsController(mContext);
+        mCallingPackage = mContext.getOpPackageName();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mAdnRecordCache = null;
+        super.tearDown();
+    }
+
+    private void fdnCheckSetup() {
+        // FDN check setup
+        doReturn(mAdnRecordCache).when(mSimRecords).getAdnCache();
+        doReturn(mUiccProfile).when(mUiccController).getUiccProfileForPhone(anyInt());
+        doReturn(true).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+        doReturn(true).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+        doReturn(false).when(mTelephonyManager).isEmergencyNumber(anyString());
+        doReturn("us").when(mTelephonyManager).getSimCountryIso();
+        doReturn(smscAddrStr).when(mIccSmsInterfaceManager).getSmscAddressFromIccEf(anyString());
+    }
+
+    private void fdnCheckCleanup() {
+        doReturn(false).when(mUiccCardApplication3gpp).getIccFdnAvailable();
+        doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+    }
+
+    @Test
+    public void isNumberBlockedByFdn_fdnListHasBothDestAddrAndSmscAddr() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+        // FDN list has both destination addr and smsc addr
+        AdnRecord smscAddrRecord = new AdnRecord(null, smscAddrStr);
+        AdnRecord destAddrRecord = new AdnRecord(null, "1234");
+        fdnList.add(0, smscAddrRecord);
+        fdnList.add(1, destAddrRecord);
+
+        // Returns false as list contains both dest addr and smsc addr
+        assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+                mCallingPackage));
+
+        // Clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void isNumberBlockedByFdn_fdnListHasDestAddr() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+        // FDN list has only destination addr
+        AdnRecord destAddrRecord = new AdnRecord(null, "1234");
+        fdnList.add(0, destAddrRecord);
+
+        // Returns true as list does not contain smsc addr
+        assertTrue(mSmsControllerUT.isNumberBlockedByFDN(1, "1234", mCallingPackage));
+
+        // Clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void isNumberBlockedByFdn_fdnListHasSmscAddr() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+        // FDN list has both destination addr and smsc addr
+        AdnRecord smscAddrRecord = new AdnRecord(null, smscAddrStr);
+        fdnList.add(0, smscAddrRecord);
+
+        // Returns true as list does not contain dest addr
+        assertTrue(mSmsControllerUT.isNumberBlockedByFDN(1, "1234", mCallingPackage));
+
+        // Clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void isNumberBlockedByFdn_destAddrIsEmergencyNumber() {
+        // FDN check setup
+        fdnCheckSetup();
+        ArrayList<AdnRecord> fdnList = new ArrayList<>();
+        doReturn(fdnList).when(mAdnRecordCache).getRecordsIfLoaded(IccConstants.EF_FDN);
+
+        doReturn(true).when(mTelephonyManager).isEmergencyNumber(anyString());
+        // Returns false as dest addr is emergency number
+        assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+                mCallingPackage));
+
+        // Clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void isNumberBlockedByFdn_fdnDisabled() {
+        // FDN check setup
+        fdnCheckSetup();
+
+        doReturn(false).when(mUiccCardApplication3gpp).getIccFdnEnabled();
+        // Returns false as fdn is not enabled
+        assertFalse(mSmsControllerUT.isNumberBlockedByFDN(1, "1234",
+                mCallingPackage));
+
+        // Clean up
+        fdnCheckCleanup();
+    }
+
+    @Test
+    public void sendVisualVoicemailSmsForSubscriber_phoneIsNotInEcm() {
+        assertFalse(mPhone.isInEcm());
+
+        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
+                1, null, 0, null, null);
+        verify(mIccSmsInterfaceManager).sendTextWithSelfPermissions(any(),
+                any(), any(), any(), any(), any(), any(), eq(false), eq(true));
+    }
+
+    @Test
+    public void sendVisualVoicemailSmsForSubscriber_phoneIsInEcm() {
+        doReturn(true).when(mPhone).isInEcm();
+
+        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
+                1, null, 0, null, null);
+        verify(mIccSmsInterfaceManager, never()).sendTextWithSelfPermissions(any(),
+                any(), any(), any(), any(), any(), any(), eq(false), eq(true));
+
+        doReturn(false).when(mPhone).isInEcm();
+    }
+
+    @Test
+    public void sendsendTextForSubscriberTest() {
+        mSmsControllerUT.sendTextForSubscriber(1, mCallingPackage, null, "1234",
+                null, "text", null, null, false, 0L, true, true);
+        verify(mIccSmsInterfaceManager, Mockito.times(1))
+                .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 6ef8508..64c2154 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -35,6 +35,7 @@
 import android.app.ActivityManager;
 import android.os.Message;
 import android.provider.Telephony.Sms.Intents;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.SmsManager;
 import android.test.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -42,11 +43,14 @@
 import android.testing.TestableLooper;
 import android.util.Singleton;
 
+import com.android.internal.telephony.uicc.IccUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.util.HashMap;
 
@@ -179,6 +183,24 @@
         assertEquals(true, mInjectionCallbackTriggered);
     }
 
+    @Test @SmallTest
+    public void testSendImsGmsTestWithSmsc() {
+        IccSmsInterfaceManager iccSmsInterfaceManager = Mockito.mock(IccSmsInterfaceManager.class);
+        when(mPhone.getIccSmsInterfaceManager()).thenReturn(iccSmsInterfaceManager);
+        when(iccSmsInterfaceManager.getSmscAddressFromIccEf("com.android.messaging"))
+                .thenReturn("222");
+        switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
+
+        mSmsDispatchersController.sendText("111", null /*scAddr*/, TAG,
+                null, null, null, "com.android.messaging",
+                false, -1, false, -1, false, 0L);
+        byte[] smscbyte = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
+                "222");
+        String smsc = IccUtils.bytesToHexString(smscbyte);
+        verify(mSimulatedCommandsVerifier).sendImsGsmSms(eq(smsc), anyString(),
+                anyInt(), anyInt(), any(Message.class));
+    }
+
     private void switchImsSmsFormat(int phoneType) {
         mSimulatedCommands.setImsRegistrationState(new int[]{1, phoneType});
         mSimulatedCommands.notifyImsNetworkStateChanged();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SsDomainControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SsDomainControllerTest.java
new file mode 100644
index 0000000..82dba86
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SsDomainControllerTest.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.IWLAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_ACR;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_ALL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BAIC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BAOC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BIC_ROAM;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BIL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BOIC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_IBS;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CB_OBS;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_ALL;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFB;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFNRC;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFNRY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CF_CFU;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR;
+
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.SsDomainController.CB_FACILITY_ACR;
+import static com.android.internal.telephony.SsDomainController.CB_FACILITY_BIL;
+import static com.android.internal.telephony.SsDomainController.SS_CLIP;
+import static com.android.internal.telephony.SsDomainController.SS_CLIR;
+import static com.android.internal.telephony.SsDomainController.SS_COLP;
+import static com.android.internal.telephony.SsDomainController.SS_COLR;
+import static com.android.internal.telephony.SsDomainController.SS_CW;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SsDomainControllerTest extends TelephonyTest {
+    private static final int[] UT_OVER_ALL = new int[] {
+            NGRAN,
+            EUTRAN,
+            IWLAN,
+            UTRAN,
+            GERAN
+    };
+
+    private static final int[]  UT_OVER_LTE_WIFI = new int[] {
+            EUTRAN,
+            IWLAN
+    };
+
+    private Map<String, String> mFacilities  = new HashMap<String, String>() {{
+            put(CB_FACILITY_BAOC, "33");
+            put(CB_FACILITY_BAOIC, "331");
+            put(CB_FACILITY_BAOICxH, "332");
+            put(CB_FACILITY_BAIC, "35");
+            put(CB_FACILITY_BAICr, "351");
+            put(CB_FACILITY_BA_ALL, "330");
+            put(CB_FACILITY_BA_MO, "333");
+            put(CB_FACILITY_BA_MT, "353");
+            put(CB_FACILITY_BIL, "156");
+            put(CB_FACILITY_ACR, "157");
+        }};
+
+    private Map<Integer, String> mReasons  = new HashMap<Integer, String>() {{
+            put(CF_REASON_ALL, "002");
+            put(CF_REASON_UNCONDITIONAL, "21");
+            put(CF_REASON_BUSY, "67");
+            put(CF_REASON_NOT_REACHABLE, "62");
+            put(CF_REASON_NO_REPLY, "61");
+            put(CF_REASON_ALL_CONDITIONAL, "004");
+        }};
+
+    private Map<String, String> mServices  = new HashMap<String, String>() {{
+            put(SS_CW, "43");
+            put(SS_CLIP, "30");
+            put(SS_CLIR, "31");
+            put(SS_COLP, "76");
+            put(SS_COLR, "77");
+        }};
+
+    private SsDomainController mSdc;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(this.getClass().getSimpleName());
+
+        mSdc = new SsDomainController(mPhone);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSdc = null;
+        super.tearDown();
+    }
+
+    private void verifyCb(String facility) {
+        for (String f : mFacilities.keySet()) {
+            String sc = mFacilities.get(f);
+
+            SsDomainController.SuppServiceRoutingInfo ssCode =
+                    ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+            assertNotNull(f + ", only " + facility + " available", ssCode);
+            if (TextUtils.equals(f, facility)) {
+                assertTrue(f + ", only " + facility + " available", mSdc.useCbOverUt(f));
+                assertTrue(f + ", only " + facility + " available", ssCode.useSsOverUt());
+            } else {
+                assertFalse(f + ", only " + facility + " available", mSdc.useCbOverUt(f));
+                assertFalse(f + ", only " + facility + " available", ssCode.useSsOverUt());
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testUseCbOverUt() {
+        setUtEnabled();
+        updateCarrierConfig(new int[] {});
+
+        verifyCb("");
+
+        /** barring_of_all_outgoing_calls (BAOC) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BAOC });
+
+        verifyCb(CB_FACILITY_BAOC);
+
+        /** barring_of_outgoing_international_calls (BOIC) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BOIC });
+
+        verifyCb(CB_FACILITY_BAOIC);
+
+        /** barring_of_outgoing_international_calls_except_to_home_plmn (BoICExHC) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC });
+
+        verifyCb(CB_FACILITY_BAOICxH);
+
+        /** barring of all incoming calls (BAIC) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BAIC });
+
+        verifyCb(CB_FACILITY_BAIC);
+
+        /** barring of incoming calls when roaming outside home PLMN Country (BICRoam) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BIC_ROAM });
+
+        verifyCb(CB_FACILITY_BAICr);
+
+        /** barring list of incoming numbers (bil) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_BIL });
+
+        verifyCb(CB_FACILITY_BIL);
+
+        /** barring of all anonymous incoming number (acr) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_ACR });
+
+        verifyCb(CB_FACILITY_ACR);
+
+        /** all barring services(BA_ALL) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_ALL });
+
+        verifyCb(CB_FACILITY_BA_ALL);
+
+        /** outgoing barring services(BA_MO) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_OBS });
+
+        verifyCb(CB_FACILITY_BA_MO);
+
+        /** incoming barring services(BA_MT) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CB_IBS });
+
+        verifyCb(CB_FACILITY_BA_MT);
+    }
+
+    private void verifyCf(int reason) {
+        for (Integer r : mReasons.keySet()) {
+            String sc = mReasons.get(r);
+
+            SsDomainController.SuppServiceRoutingInfo ssCode =
+                    ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+            assertNotNull(r + ", only " + reason + " available", ssCode);
+            if (r == reason) {
+                assertTrue(r + ", only " + reason + " available", mSdc.useCfOverUt(r));
+                assertTrue(r + ", only " + reason + " available", ssCode.useSsOverUt());
+            } else {
+                assertFalse(r + ", only " + reason + " available", mSdc.useCfOverUt(r));
+                assertFalse(r + ", only " + reason + " available", ssCode.useSsOverUt());
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testUseCfOverUt() {
+        setUtEnabled();
+        updateCarrierConfig(new int[] {});
+
+        verifyCf(-1);
+
+        /** all_call_forwarding (CFAll) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_ALL });
+
+        verifyCf(CF_REASON_ALL);
+
+        /** all_forwarding_unconditional (CFU) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_CFU });
+
+        verifyCf(CF_REASON_UNCONDITIONAL);
+
+        /** all_call_conditional_forwarding (allCondForwarding) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING });
+
+        verifyCf(CF_REASON_ALL_CONDITIONAL);
+
+        /** call_forwarding_on_mobile_subscriber_busy (CFB) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_CFB });
+
+        verifyCf(CF_REASON_BUSY);
+
+        /** call_forwarding_on_no_reply (CFNRY) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_CFNRY });
+
+        verifyCf(CF_REASON_NO_REPLY);
+
+        /** call_forwarding_on_mobile_subscriber_unreachable (CFNRC) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CF_CFNRC });
+
+        verifyCf(CF_REASON_NOT_REACHABLE);
+    }
+
+    private void verifySs(String service) {
+        for (String s : mServices.keySet()) {
+            String sc = mServices.get(s);
+
+            SsDomainController.SuppServiceRoutingInfo ssCode =
+                    ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+            assertNotNull(s + ", only " + service + " available", ssCode);
+            if (TextUtils.equals(s, service)) {
+                assertTrue(s + ", only " + service + " available", mSdc.useSsOverUt(s));
+                assertTrue(s + ", only " + service + " available", ssCode.useSsOverUt());
+            } else {
+                assertFalse(s + ", only " + service + " available", mSdc.useSsOverUt(s));
+                assertFalse(s + ", only " + service + " available", ssCode.useSsOverUt());
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testUseSsOverUt() {
+        setUtEnabled();
+        updateCarrierConfig(new int[] {});
+        verifySs("");
+
+        /** call waiting (CW) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_CW });
+
+        verifySs(SS_CW);
+
+        /** OIP (clip) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP });
+
+        verifySs(SS_CLIP);
+
+        /** TIP (colp) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP });
+
+        verifySs(SS_COLP);
+
+        /** TIR (colr) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR });
+
+        verifySs(SS_COLR);
+
+        /** OIR (clir) */
+        updateCarrierConfig(new int[] { SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR });
+
+        verifySs(SS_CLIR);
+    }
+
+    @Test
+    @SmallTest
+    public void testUtEnabled() {
+        doReturn(0).when(mImsPhone).getSubId();
+        mSdc.updateWifiForUt(false);
+
+        ServiceState imsSs = new ServiceState();
+        // IMS is not registered
+        imsSs.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(imsSs).when(mImsPhone).getServiceState();
+
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+        ServiceState ss = new ServiceState();
+        // IMS is not registered
+        ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(ss).when(mPhone).getServiceState();
+
+        // WWAN_LEGACY is registered
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+                .build();
+
+        ss.addNetworkRegistrationInfo(nri);
+
+        // IMS Registration is NOT required, enabled when roaming, transport = ALL
+        updateCarrierConfig(false, true, UT_OVER_ALL);
+
+        assertTrue(mSdc.isUtEnabled());
+
+        // IMS Registration is NOT required, enabled when roaming, transport = LTE | WiFi
+        updateCarrierConfig(false, true, UT_OVER_LTE_WIFI);
+
+        // Ut is not available over 3G and 2G.
+        assertFalse(mSdc.isUtEnabled());
+
+        // Wi-Fi is connected
+        mSdc.updateWifiForUt(true);
+
+        // Ut is available over WiFi.
+        assertTrue(mSdc.isUtEnabled());
+
+        // IMS Registration is REQUIRED, enabled when roaming, transport = LTE | WiFi
+        updateCarrierConfig(true, true, UT_OVER_LTE_WIFI);
+
+        // IMS is not registered.
+        assertFalse(mSdc.isUtEnabled());
+
+        // IMS is registered
+        imsSs.setState(ServiceState.STATE_IN_SERVICE);
+
+        assertTrue(mSdc.isUtEnabled());
+    }
+
+    @Test
+    @SmallTest
+    public void testUtWhenRoaming() {
+        doReturn(0).when(mImsPhone).getSubId();
+        mSdc.updateWifiForUt(false);
+
+        ServiceState imsSs = new ServiceState();
+        // IMS is not registered
+        imsSs.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(imsSs).when(mImsPhone).getServiceState();
+
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+        ServiceState ss = new ServiceState();
+        // IMS is not registered
+        ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(ss).when(mPhone).getServiceState();
+
+        // WWAN_LEGACY is registered
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+                .build();
+
+        ss.addNetworkRegistrationInfo(nri);
+
+        // IMS Registration is NOT required, enabled when roaming, transport = ALL
+        updateCarrierConfig(false, true, UT_OVER_ALL);
+
+        assertTrue(mSdc.isUtEnabled());
+
+        // IMS Registration is NOT required, disabled when roaming, transport = ALL
+        updateCarrierConfig(false, false, UT_OVER_ALL);
+
+        // Ut is not available when roaming
+        assertFalse(mSdc.isUtEnabled());
+    }
+
+    @Test
+    @SmallTest
+    public void testOemHandlesTerminalBasedCallWaiting() {
+        setUtEnabled();
+
+        // Enable terminal-based call waiting
+        mSdc.updateCarrierConfigForTest(true, true, false, true, true,
+                new int[] {}, UT_OVER_ALL, new int[] { SUPPLEMENTARY_SERVICE_CW });
+        String sc = mServices.get(SS_CW);
+
+        mSdc.setOemHandlesTerminalBasedService(false);
+        SsDomainController.SuppServiceRoutingInfo ssCode =
+                ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+        assertNotNull(ssCode);
+        assertFalse(ssCode.useSsOverUt());
+
+        mSdc.setOemHandlesTerminalBasedService(true);
+        ssCode = ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+
+        assertNotNull(ssCode);
+        assertTrue(ssCode.useSsOverUt());
+    }
+
+    @Test
+    @SmallTest
+    public void testOemHandlesTerminalBasedClir() {
+        setUtEnabled();
+
+        // Enable terminal-based CLIR
+        mSdc.updateCarrierConfigForTest(true, true, false, true, true,
+                new int[] {}, UT_OVER_ALL, new int[] { SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR });
+        String sc = mServices.get(SS_CLIR);
+
+        mSdc.setOemHandlesTerminalBasedService(false);
+        SsDomainController.SuppServiceRoutingInfo ssCode =
+                ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+
+        assertNotNull(ssCode);
+        assertFalse(ssCode.useSsOverUt());
+        assertFalse(mSdc.getOemHandlesTerminalBasedClir());
+
+        mSdc.setOemHandlesTerminalBasedService(true);
+        ssCode = ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+
+        assertNotNull(ssCode);
+        assertTrue(ssCode.useSsOverUt());
+        assertTrue(mSdc.getOemHandlesTerminalBasedClir());
+
+        // Disable terminal-based CLIR
+        mSdc.updateCarrierConfigForTest(true, true, false, true, true,
+                new int[] {}, UT_OVER_ALL, new int[] {});
+        ssCode = ImsPhoneMmiCode.getSuppServiceRoutingInfo("*#" + sc + "#", mSdc);
+
+        assertNotNull(ssCode);
+        assertFalse(ssCode.useSsOverUt());
+        assertFalse(mSdc.getOemHandlesTerminalBasedClir());
+    }
+
+    private void setUtEnabled() {
+        doReturn(0).when(mImsPhone).getSubId();
+        mSdc.updateWifiForUt(false);
+
+        ServiceState imsSs = new ServiceState();
+        // IMS is not registered
+        imsSs.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(imsSs).when(mImsPhone).getServiceState();
+
+        doReturn(true).when(mImsPhone).isUtEnabled();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+        ServiceState ss = new ServiceState();
+        // IMS is not registered
+        ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        doReturn(ss).when(mPhone).getServiceState();
+
+        // WWAN_LEGACY is registered
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+                .build();
+
+        ss.addNetworkRegistrationInfo(nri);
+    }
+
+    private void updateCarrierConfig(boolean requiresImsRegistration,
+            boolean availableWhenRoaming, int[] utRats) {
+        updateCarrierConfig(true, requiresImsRegistration, true, availableWhenRoaming, utRats);
+    }
+
+    private void updateCarrierConfig(boolean supportsCsfb, boolean requiresImsRegistration,
+            boolean availableWhenPsDataOff, boolean availableWhenRoaming, int[] utRats) {
+        mSdc.updateCarrierConfigForTest(true, supportsCsfb, requiresImsRegistration,
+                availableWhenPsDataOff, availableWhenRoaming, null, utRats, null);
+    }
+
+    private void updateCarrierConfig(int[] services) {
+        mSdc.updateCarrierConfigForTest(true, true, false, true, true, services, UT_OVER_ALL, null);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 38a2454..5b8bdaa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -19,6 +19,7 @@
 
 import static com.android.internal.telephony.SubscriptionController.REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID;
 import static com.android.internal.telephony.uicc.IccCardStatus.CardState.CARDSTATE_PRESENT;
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -33,7 +34,6 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
@@ -284,12 +284,6 @@
         assertEquals(iconTint, subInfo.getIconTint());
         assertEquals(disNum, subInfo.getNumber());
         assertEquals(isOpportunistic, subInfo.isOpportunistic());
-
-        /* verify broadcast intent */
-        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
-        assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
-                captorIntent.getValue().getAction());
     }
 
     @Test @SmallTest
@@ -309,14 +303,7 @@
                 .getActiveSubscriptionInfo(subID, mCallingPackage, mCallingFeature);
         assertNotNull(subInfo);
         assertEquals(disName, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
-
-        /* verify broadcast intent */
-        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
-        assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
-                captorIntent.getValue().getAction());
-
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     private void setSimEmbedded(boolean isEmbedded) throws Exception {
@@ -350,7 +337,7 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
@@ -377,7 +364,7 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
@@ -404,7 +391,7 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
@@ -435,7 +422,7 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
@@ -461,7 +448,7 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
@@ -486,13 +473,13 @@
 
         assertNotNull(subInfo);
         assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
-        assertEquals(nameSource, subInfo.getNameSource());
+        assertEquals(nameSource, subInfo.getDisplayNameSource());
     }
 
     @Test @SmallTest
     public void testIsExistingNameSourceStillValid_pnnIsNotNull_returnTrue() {
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_PNN);
         when(mPhone.getPlmn()).thenReturn("testing_pnn");
 
@@ -502,7 +489,7 @@
     @Test @SmallTest
     public void testIsExistingNameSourceStillValid_spnIsNotNull_returnTrue() {
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_SPN);
         when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
         when(mUiccProfile.getServiceProviderName()).thenReturn("testing_spn");
@@ -514,7 +501,7 @@
     public void testIsExistingNameSourceStillValid_simIsEmbedded_returnTrue() {
         when(mMockSubscriptionInfo.isEmbedded()).thenReturn(true);
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
 
         assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
@@ -524,7 +511,7 @@
     public void testIsExistingNameSourceStillValid_carrierConfigIsNull_returnTrue() {
         when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
         when(mCarrierConfigManager.getConfigForSubId(FAKE_SUBID)).thenReturn(null);
 
@@ -535,7 +522,7 @@
     public void testIsExistingNameSourceStillValid_carrierNameOverrideIsTrue_returnTrue() {
         when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
         mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
 
@@ -546,7 +533,7 @@
     public void testIsExistingNameSourceStillValid_spnIsNullAndCarrierNameIsNotNull_returnTrue() {
         when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
         when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
-        when(mMockSubscriptionInfo.getNameSource())
+        when(mMockSubscriptionInfo.getDisplayNameSource())
                 .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
         when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
         when(mUiccProfile.getServiceProviderName()).thenReturn(null);
@@ -595,12 +582,6 @@
         assertNotNull(subInfo);
         assertEquals(Integer.parseInt(mCcMncVERIZON.substring(0, 3)), subInfo.getMcc());
         assertEquals(Integer.parseInt(mCcMncVERIZON.substring(3)), subInfo.getMnc());
-
-         /* verify broadcast intent */
-        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
-        assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
-                captorIntent.getValue().getAction());
     }
 
     @Test @SmallTest
@@ -613,12 +594,6 @@
                 .getActiveSubscriptionInfo(1, mCallingPackage, mCallingFeature);
         assertNotNull(subInfo);
         assertEquals(carrierId, subInfo.getCarrierId());
-
-         /* verify broadcast intent */
-        ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
-        assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
-                captorIntent.getValue().getAction());
     }
 
     @Test
@@ -1755,7 +1730,7 @@
         } catch (SecurityException expected) {
         }
 
-        // only has the device identifiers permission
+        // only has the USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER permission
         setIdentifierAccess(true);
         try {
             mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
@@ -1768,10 +1743,13 @@
         // only has the READ_PHONE_STATE permission
         setIdentifierAccess(false);
         setReadPhoneState();
-        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
-                groupUuid, mCallingPackage, mCallingFeature);
-        assertNotNull(subInfoList);
-        assertTrue(subInfoList.isEmpty());
+        try {
+            mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
+                    mCallingFeature);
+            fail("getSubscriptionsInGroup should fail when invoked with no "
+                    + "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER permissions");
+        } catch (SecurityException expected) {
+        }
     }
 
     @Test
@@ -2121,4 +2099,87 @@
         assertTrue(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test2"));
         assertFalse(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test3"));
     }
-}
+
+    @Test
+    @SmallTest
+    public void testMessageRefDBFetchAndUpdate() throws Exception {
+        testInsertSim();
+        int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
+        assertTrue(subIds != null && subIds.length != 0);
+        final int subId = subIds[0];
+        SubscriptionController.getInstance().updateMessageRef(subId, 201);
+        int messageRef = SubscriptionController.getInstance().getMessageRef(subId);
+        assertTrue("201 :", messageRef == 201);
+    }
+
+    @Test
+    public void setSubscriptionUserHandle_withoutPermission() {
+        testInsertSim();
+        /* Get SUB ID */
+        int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
+        assertTrue(subIds != null && subIds.length != 0);
+        final int subId = subIds[0];
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionControllerUT.setSubscriptionUserHandle(
+                        UserHandle.of(UserHandle.USER_SYSTEM), subId));
+    }
+
+    @Test
+    public void setGetSubscriptionUserHandle_userHandleNull() {
+        testInsertSim();
+        /* Get SUB ID */
+        int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
+        assertTrue(subIds != null && subIds.length != 0);
+        final int subId = subIds[0];
+
+        mSubscriptionControllerUT.setSubscriptionUserHandle(null, subId);
+
+        assertThat(mSubscriptionControllerUT.getSubscriptionUserHandle(subId))
+                .isEqualTo(null);
+    }
+
+    @Test
+    public void setSubscriptionUserHandle_invalidSubId() {
+        assertThrows(IllegalArgumentException.class,
+                () -> mSubscriptionControllerUT.setSubscriptionUserHandle(
+                        UserHandle.of(UserHandle.USER_SYSTEM),
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID));
+    }
+
+    @Test
+    public void setGetSubscriptionUserHandle_withValidUserHandleAndSubId() {
+        testInsertSim();
+        /* Get SUB ID */
+        int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
+        assertTrue(subIds != null && subIds.length != 0);
+        final int subId = subIds[0];
+
+        mSubscriptionControllerUT.setSubscriptionUserHandle(
+                UserHandle.of(UserHandle.USER_SYSTEM), subId);
+
+        assertThat(mSubscriptionControllerUT.getSubscriptionUserHandle(subId))
+                .isEqualTo(UserHandle.of(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void getSubscriptionUserHandle_withoutPermission() {
+        testInsertSim();
+        /* Get SUB ID */
+        int[] subIds = mSubscriptionControllerUT.getActiveSubIdList(/*visibleOnly*/false);
+        assertTrue(subIds != null && subIds.length != 0);
+        final int subId = subIds[0];
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionControllerUT.getSubscriptionUserHandle(subId));
+    }
+
+    @Test
+    public void getSubscriptionUserHandle_invalidSubId() {
+        assertThrows(IllegalArgumentException.class,
+                () -> mSubscriptionControllerUT.getSubscriptionUserHandle(
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID));
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index 3f85435..3bafe4d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -15,12 +15,11 @@
  */
 package com.android.internal.telephony;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
 import android.telephony.SubscriptionInfo;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.telephony.SubscriptionManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -38,78 +37,63 @@
 
     @Before
     public void setUp() throws Exception {
-        mSubscriptionInfoUT = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
-                "T-mobile", 0, 255, "12345", 0, null, "310", "260", "156", false, null, null);
-        mSubscriptionInfoUT.setAssociatedPlmns(EHPLMNS, HPLMNS);
+        mSubscriptionInfoUT = new SubscriptionInfo.Builder()
+                .setId(1)
+                .setIccId("890126042XXXXXXXXXXX")
+                .setSimSlotIndex(0)
+                .setDisplayName("T-mobile")
+                .setCarrierName("T-mobile")
+                .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER_ID)
+                .setIconTint(255)
+                .setNumber("12345")
+                .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE)
+                .setMcc("310")
+                .setMnc("260")
+                .setEhplmns(EHPLMNS)
+                .setHplmns(HPLMNS)
+                .setCountryIso("us")
+                .build();
     }
 
     @Test
-    @SmallTest
     public void testSubProperties() {
-        assertEquals(260, mSubscriptionInfoUT.getMnc());
-        assertEquals(310, mSubscriptionInfoUT.getMcc());
-        assertEquals("12345", mSubscriptionInfoUT.getNumber());
-        assertEquals(0, mSubscriptionInfoUT.getDataRoaming());
-        assertEquals("T-mobile", mSubscriptionInfoUT.getDisplayName());
-        assertEquals("T-mobile", mSubscriptionInfoUT.getCarrierName());
-        assertEquals("156", mSubscriptionInfoUT.getCountryIso());
-        assertEquals(255, mSubscriptionInfoUT.getIconTint());
-        assertEquals(0, mSubscriptionInfoUT.getNameSource());
-        assertEquals(1, mSubscriptionInfoUT.getSubscriptionId());
-        assertEquals(0, mSubscriptionInfoUT.getSimSlotIndex());
-        assertEquals("890126042XXXXXXXXXXX", mSubscriptionInfoUT.getIccId());
+        assertThat(mSubscriptionInfoUT.getMcc()).isEqualTo(310);
+        assertThat(mSubscriptionInfoUT.getMccString()).isEqualTo("310");
+        assertThat(mSubscriptionInfoUT.getMnc()).isEqualTo(260);
+        assertThat(mSubscriptionInfoUT.getMncString()).isEqualTo("260");
+        assertThat(mSubscriptionInfoUT.getNumber()).isEqualTo("12345");
+        assertThat(mSubscriptionInfoUT.getDataRoaming()).isEqualTo(0);
+        assertThat(mSubscriptionInfoUT.getDisplayName().toString()).isEqualTo("T-mobile");
+        assertThat(mSubscriptionInfoUT.getCarrierName().toString()).isEqualTo("T-mobile");
+        assertThat(mSubscriptionInfoUT.getCountryIso()).isEqualTo("us");
+        assertThat(mSubscriptionInfoUT.getIconTint()).isEqualTo(255);
+        assertThat(mSubscriptionInfoUT.getDisplayNameSource()).isEqualTo(0);
+        assertThat(mSubscriptionInfoUT.getSubscriptionId()).isEqualTo(1);
+        assertThat(mSubscriptionInfoUT.getSimSlotIndex()).isEqualTo(0);
+        assertThat(mSubscriptionInfoUT.getIccId()).isEqualTo("890126042XXXXXXXXXXX");
     }
 
     @Test
-    @SmallTest
-    public void testSetGetCarrierName() {
-        assertEquals("T-mobile", mSubscriptionInfoUT.getCarrierName());
-        mSubscriptionInfoUT.setCarrierName("Verizon");
-        assertEquals("Verizon", mSubscriptionInfoUT.getCarrierName());
-    }
-
-    @Test
-    @SmallTest
-    public void testSetGetDisplayName() {
-        assertEquals("T-mobile", mSubscriptionInfoUT.getDisplayName());
-        mSubscriptionInfoUT.setDisplayName("Verizon");
-        assertEquals("Verizon", mSubscriptionInfoUT.getDisplayName());
-    }
-
-    @Test
-    @SmallTest
-    public void testSetGetIconTint() {
-        assertEquals(255, mSubscriptionInfoUT.getIconTint());
-        mSubscriptionInfoUT.setIconTint(0);
-        assertEquals(0, mSubscriptionInfoUT.getIconTint());
-    }
-
-    @Test
-    @SmallTest
     public void testParcelUnparcel() {
         Parcel p = Parcel.obtain();
         mSubscriptionInfoUT.writeToParcel(p, 0);
         p.setDataPosition(0);
         SubscriptionInfo copy = SubscriptionInfo.CREATOR.createFromParcel(p);
-        assertEquals(mSubscriptionInfoUT, copy);
+        assertThat(mSubscriptionInfoUT).isEqualTo(copy);
     }
 
     @Test
-    @SmallTest
     public void testEquals() {
-        SubscriptionInfo copiedInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
-                "T-mobile", "T-mobile", 0, 255, "12345", 0, null,
-                "310", "260", "156", false, null, null);
-        copiedInfo.setAssociatedPlmns(EHPLMNS, HPLMNS);
-        SubscriptionInfo differentDisplayName = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
-                "AT&T", "T-mobile", 0, 255, "12345", 0, null,
-                "310", "260", "156", false, null, null);
-        SubscriptionInfo differentSubId = new SubscriptionInfo(2, "890126042XXXXXXXXXXX", 0,
-                "AT&T", "T-mobile", 0, 255, "12345", 0, null,
-                "310", "260", "156", false, null, null);
+        SubscriptionInfo copiedInfo = new SubscriptionInfo.Builder(mSubscriptionInfoUT).build();
+        SubscriptionInfo differentDisplayName = new SubscriptionInfo.Builder(mSubscriptionInfoUT)
+                .setDisplayName("Different display name")
+                .build();
+        SubscriptionInfo differentSubId = new SubscriptionInfo.Builder(mSubscriptionInfoUT)
+                .setId(1234)
+                .build();
 
-        assertEquals(mSubscriptionInfoUT, copiedInfo);
-        assertNotEquals(mSubscriptionInfoUT, differentDisplayName);
-        assertNotEquals(mSubscriptionInfoUT, differentSubId);
+        assertThat(mSubscriptionInfoUT).isEqualTo(copiedInfo);
+        assertThat(mSubscriptionInfoUT).isNotEqualTo(differentDisplayName);
+        assertThat(mSubscriptionInfoUT).isNotEqualTo(differentSubId);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index c669810..4937529 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -601,13 +601,16 @@
 
         List<SubscriptionInfo> subInfoList = new ArrayList<>();
         // 1: not embedded, but has matching iccid with an embedded subscription.
-        subInfoList.add(new SubscriptionInfo(
-                        0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
-                        null /* accessRules */, null));
+        subInfoList.add(new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setIccId("1")
+                .build());
         // 2: embedded but no longer present.
-        subInfoList.add(new SubscriptionInfo(
-                0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
-                null /* accessRules */, null));
+        subInfoList.add(new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setIccId("2")
+                .setEmbedded(true)
+                .build());
 
         when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                 new String[] { "1", "3"}, false /* removable */)).thenReturn(subInfoList);
@@ -655,13 +658,16 @@
 
         List<SubscriptionInfo> subInfoList = new ArrayList<>();
         // 1: not embedded, but has matching iccid with an embedded subscription.
-        subInfoList.add(new SubscriptionInfo(
-                0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
-                null /* accessRules */, null));
+        subInfoList.add(new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setIccId("1")
+                .build());
         // 2: embedded.
-        subInfoList.add(new SubscriptionInfo(
-                0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
-                null /* accessRules */, null));
+        subInfoList.add(new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setIccId("2")
+                .setEmbedded(true)
+                .build());
 
         when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                 new String[0], false /* removable */)).thenReturn(subInfoList);
@@ -689,9 +695,10 @@
 
         List<SubscriptionInfo> subInfoList = new ArrayList<>();
         // 1: not embedded.
-        subInfoList.add(new SubscriptionInfo(
-                0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
-                null /* accessRules */, null));
+        subInfoList.add(new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setIccId("1")
+                .build());
 
         when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
                 new String[0], false /* removable */)).thenReturn(subInfoList);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 6aed4cf..b0b72e4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -97,16 +97,12 @@
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.CellularNetworkValidator;
 import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.DataEnabledOverride;
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataProfileManager;
 import com.android.internal.telephony.data.DataRetryManager;
 import com.android.internal.telephony.data.DataServiceManager;
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DataThrottler;
-import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -193,7 +189,6 @@
     protected RegistrantList mRegistrantList;
     protected IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
     protected ImsManager mImsManager;
-    protected DcTracker mDcTracker;
     protected DataNetworkController mDataNetworkController;
     protected DataRetryManager mDataRetryManager;
     protected DataSettingsManager mDataSettingsManager;
@@ -241,8 +236,6 @@
     protected SubscriptionInfoUpdater mSubInfoRecordUpdater;
     protected LocaleTracker mLocaleTracker;
     protected RestrictedState mRestrictedState;
-    protected DataEnabledSettings mDataEnabledSettings;
-    protected DataEnabledOverride mDataEnabledOverride;
     protected PhoneConfigurationManager mPhoneConfigurationManager;
     protected CellularNetworkValidator mCellularNetworkValidator;
     protected UiccCard mUiccCard;
@@ -255,7 +248,6 @@
     protected PersistAtomsStorage mPersistAtomsStorage;
     protected MetricsCollector mMetricsCollector;
     protected SmsStats mSmsStats;
-    protected DataThrottler mDataThrottler;
     protected SignalStrength mSignalStrength;
     protected WifiManager mWifiManager;
     protected WifiInfo mWifiInfo;
@@ -267,6 +259,7 @@
     protected CellLocation mCellLocation;
     protected DataServiceManager mMockedWwanDataServiceManager;
     protected DataServiceManager mMockedWlanDataServiceManager;
+    protected SsDomainController mSsDomainController;
 
     // Initialized classes
     protected ActivityManager mActivityManager;
@@ -426,7 +419,6 @@
         mRegistrantList = Mockito.mock(RegistrantList.class);
         mIccPhoneBookIntManager = Mockito.mock(IccPhoneBookInterfaceManager.class);
         mImsManager = Mockito.mock(ImsManager.class);
-        mDcTracker = Mockito.mock(DcTracker.class);
         mDataNetworkController = Mockito.mock(DataNetworkController.class);
         mDataRetryManager = Mockito.mock(DataRetryManager.class);
         mDataSettingsManager = Mockito.mock(DataSettingsManager.class);
@@ -474,8 +466,6 @@
         mSubInfoRecordUpdater = Mockito.mock(SubscriptionInfoUpdater.class);
         mLocaleTracker = Mockito.mock(LocaleTracker.class);
         mRestrictedState = Mockito.mock(RestrictedState.class);
-        mDataEnabledSettings = Mockito.mock(DataEnabledSettings.class);
-        mDataEnabledOverride = Mockito.mock(DataEnabledOverride.class);
         mPhoneConfigurationManager = Mockito.mock(PhoneConfigurationManager.class);
         mCellularNetworkValidator = Mockito.mock(CellularNetworkValidator.class);
         mUiccCard = Mockito.mock(UiccCard.class);
@@ -488,7 +478,6 @@
         mPersistAtomsStorage = Mockito.mock(PersistAtomsStorage.class);
         mMetricsCollector = Mockito.mock(MetricsCollector.class);
         mSmsStats = Mockito.mock(SmsStats.class);
-        mDataThrottler = Mockito.mock(DataThrottler.class);
         mSignalStrength = Mockito.mock(SignalStrength.class);
         mWifiManager = Mockito.mock(WifiManager.class);
         mWifiInfo = Mockito.mock(WifiInfo.class);
@@ -500,6 +489,7 @@
         mCellLocation = Mockito.mock(CellLocation.class);
         mMockedWwanDataServiceManager = Mockito.mock(DataServiceManager.class);
         mMockedWlanDataServiceManager = Mockito.mock(DataServiceManager.class);
+        mSsDomainController = Mockito.mock(SsDomainController.class);
 
         TelephonyManager.disableServiceHandleCaching();
         PropertyInvalidatedCache.disableForTestMode();
@@ -568,8 +558,6 @@
                 .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
                 .makeIccPhoneBookInterfaceManager(nullable(Phone.class));
-        doReturn(mDcTracker).when(mTelephonyComponentFactory)
-                .makeDcTracker(nullable(Phone.class), anyInt());
         doReturn(mDisplayInfoController).when(mTelephonyComponentFactory)
                 .makeDisplayInfoController(nullable(Phone.class));
         doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
@@ -597,8 +585,6 @@
         doReturn(mLocaleTracker).when(mTelephonyComponentFactory)
                 .makeLocaleTracker(nullable(Phone.class), nullable(NitzStateMachine.class),
                         nullable(Looper.class));
-        doReturn(mDataEnabledSettings).when(mTelephonyComponentFactory)
-                .makeDataEnabledSettings(nullable(Phone.class));
         doReturn(mEriManager).when(mTelephonyComponentFactory)
                 .makeEriManager(nullable(Phone.class), anyInt());
         doReturn(mLinkBandwidthEstimator).when(mTelephonyComponentFactory)
@@ -628,8 +614,6 @@
         doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
         doReturn(mIccSmsInterfaceManager).when(mPhone).getIccSmsInterfaceManager();
         doReturn(mAccessNetworksManager).when(mPhone).getAccessNetworksManager();
-        doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
-        doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
         doReturn(mDataSettingsManager).when(mDataNetworkController).getDataSettingsManager();
         doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
         doReturn(mDataSettingsManager).when(mPhone).getDataSettingsManager();
@@ -647,7 +631,6 @@
         doReturn(mDataProfileManager).when(mDataNetworkController).getDataProfileManager();
         doReturn(mDataRetryManager).when(mDataNetworkController).getDataRetryManager();
         doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
-        doReturn(true).when(mPhone).isUsingNewDataStack();
         doReturn(0).when(mPhone).getPhoneId();
 
         //mUiccController
@@ -738,15 +721,10 @@
         doReturn(new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                 AccessNetworkConstants.TRANSPORT_TYPE_WLAN})
                 .when(mAccessNetworksManager).getAvailableTransports();
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
-                .getCurrentTransport(anyInt());
-        doReturn(true).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        doReturn(true).when(mDataEnabledSettings).isInternalDataEnabled();
         doReturn(true).when(mDataSettingsManager).isDataEnabled();
         doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
                 anyInt(), anyInt());
-        doReturn(RIL.RADIO_HAL_VERSION_2_0).when(mPhone).getHalVersion();
+        doReturn(RIL.RADIO_HAL_VERSION_2_0).when(mPhone).getHalVersion(anyInt());
         doReturn(2).when(mSignalStrength).getLevel();
 
         // WiFi
@@ -778,8 +756,6 @@
         Settings.Global.putInt(resolver,
                 Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
         Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, 0);
-        doReturn(mDataThrottler).when(mDcTracker).getDataThrottler();
-        doReturn(-1L).when(mDataThrottler).getRetryTime(anyInt());
 
         doReturn(90).when(mDataConfigManager).getNetworkCapabilityPriority(
                 eq(NetworkCapabilities.NET_CAPABILITY_EIMS));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken b/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
deleted file mode 100644
index 761dfc7..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import android.telephony.CellInfo;
-
-import java.util.List;
-
-/**
- * Stub class used for unit tests
- */
-
-public class TestPhoneNotifier implements PhoneNotifier {
-    public TestPhoneNotifier() {
-    }
-
-    public void notifyPhoneState(Phone sender) {
-    }
-
-    public void notifyServiceState(Phone sender) {
-    }
-
-    public void notifyCellLocation(Phone sender) {
-    }
-
-    public void notifySignalStrength(Phone sender) {
-    }
-
-    public void notifyMessageWaitingChanged(Phone sender) {
-    }
-
-    public void notifyCallForwardingChanged(Phone sender) {
-    }
-
-    public void notifyDataConnection(Phone sender, String reason, String apnType) {
-    }
-
-    public void notifyDataConnection(Phone sender, String reason, String apnType,
-            PhoneConstants.DataState state) {
-    }
-
-    public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
-    }
-
-    public void notifyDataActivity(Phone sender) {
-    }
-
-    public void notifyOtaspChanged(Phone sender, int otaspMode) {
-    }
-
-    public void notifyCellInfo(Phone sender, List<CellInfo> cellInfo) {
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
new file mode 100644
index 0000000..6bc31da
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.cat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.telephony.SmsMessage;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.ProxyController;
+import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccFileHandler;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccProfile;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CATServiceTest extends TelephonyTest {
+
+    //Mocked Classes
+    @Mock
+    private RilMessageDecoder mRilMessageDecoder;
+    private IccFileHandler mIccFileHandler;
+    private SmsController mSmsController;
+    private CommandDetails mCommandDetails;
+    private CatService mCatService;
+    private IccCardStatus mIccCardStatus;
+    private IccIoResult mIccIoResult;
+
+    private String mData =
+            "D059810301130082028183051353656E64696E672072657175657374202E2E2E0607911989548056780B"
+                    + "3051FF05812143F500F6082502700000201115001500BFFF01BA23C2169EA9B02D7A7FBAA0"
+                    + "DAABFEE8B8DE9DA06DCD234E";
+    private byte[] mRawdata = IccUtils.hexStringToBytes(mData);
+    private List<ComprehensionTlv> mCtlvs;
+
+    public CATServiceTest() {
+        super();
+    }
+
+    private IccCardApplicationStatus composeUiccApplicationStatus(
+            IccCardApplicationStatus.AppType appType,
+            IccCardApplicationStatus.AppState appState, String aid) {
+        IccCardApplicationStatus mIccCardAppStatus = new IccCardApplicationStatus();
+        mIccCardAppStatus.aid = aid;
+        mIccCardAppStatus.app_type = appType;
+        mIccCardAppStatus.aid = aid;
+        mIccCardAppStatus.app_type = appType;
+        mIccCardAppStatus.app_state = appState;
+        mIccCardAppStatus.pin1 = mIccCardAppStatus.pin2 =
+                IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+        return mIccCardAppStatus;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mRilMessageDecoder = mock(RilMessageDecoder.class);
+        mIccFileHandler = mock(IccFileHandler.class);
+        mSmsController = mock(SmsController.class);
+        mIccCardStatus = mock(IccCardStatus.class);
+        mProxyController = mock(ProxyController.class);
+        mUiccCard = mock(UiccCard.class);
+        IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+                IccCardApplicationStatus.AppType.APPTYPE_USIM,
+                IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+        mIccCardStatus.mApplications = new IccCardApplicationStatus[]{umtsApp};
+        mIccCardStatus.mCdmaSubscriptionAppIndex =
+                mIccCardStatus.mImsSubscriptionAppIndex =
+                        mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+        mIccIoResult = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes("FF40"));
+        mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
+        mUiccProfile = new UiccProfile(mContext, mSimulatedCommands, mIccCardStatus,
+                0 /* phoneId */, mUiccCard, new Object());
+        processAllMessages();
+        logd("Created UiccProfile");
+        processAllMessages();
+        mCatService = CatService.getInstance(mSimulatedCommands, mUiccController.mContext,
+                mUiccProfile, mUiccController.getSlotIdFromPhoneId(0));
+        logd("Created CATService");
+        createCommandDetails();
+        createComprehensionTlvList();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiccProfile = null;
+        mCatService = null;
+        mCtlvs = null;
+        mProxyController = null;
+        mRilMessageDecoder = null;
+        mCommandDetails = null;
+        super.tearDown();
+    }
+
+    private void createCommandDetails() {
+        mCommandDetails = mock(CommandDetails.class);
+        mCommandDetails.compRequired = true;
+        mCommandDetails.commandNumber = 1;
+        mCommandDetails.typeOfCommand = 19;
+        mCommandDetails.commandQualifier = 0;
+    }
+
+    private void createComprehensionTlvList() {
+        ComprehensionTlv ctlv1 = new ComprehensionTlv(1, false, 3, mRawdata, 4);
+        ComprehensionTlv ctlv2 = new ComprehensionTlv(2, false, 2, mRawdata, 9);
+        ComprehensionTlv ctlv3 = new ComprehensionTlv(5, false, 19, mRawdata, 13);
+        ComprehensionTlv ctlv4 = new ComprehensionTlv(6, false, 7, mRawdata, 34);
+        ComprehensionTlv ctlv5 = new ComprehensionTlv(11, false, 48, mRawdata, 43);
+        mCtlvs = new ArrayList<>();
+        mCtlvs.add(ctlv1);
+        mCtlvs.add(ctlv2);
+        mCtlvs.add(ctlv3);
+        mCtlvs.add(ctlv4);
+        mCtlvs.add(ctlv5);
+    }
+
+    @Test
+    public void testSendSmsCommandParams() throws Exception {
+        ComprehensionTlv ctlv = new ComprehensionTlv(11, false, 48, mRawdata, 43);
+        SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlv);
+        assertNotNull(smsMessage);
+        assertEquals("12345", smsMessage.getRecipientAddress());
+    }
+
+    @Test
+    public void testSendSTKSmsViaCatService() {
+        CommandParams cmdPrms = new CommandParams(mCommandDetails);
+        when(mProxyController.getSmsController()).thenReturn(mSmsController);
+        mCatService.sendStkSms("test", "12345", 1, cmdPrms, mProxyController);
+        verify(mSmsController, Mockito.times(1)).sendTextForSubscriber(anyInt(),
+                anyString(), nullable(String.class), anyString(), nullable(String.class),
+                anyString(), Mockito.anyObject(), any(), eq(false), anyLong(), eq(true), eq(true));
+    }
+
+    @Test
+    public void testprocessSMSEventNotify() throws Exception {
+        CommandParamsFactory cmdPF = CommandParamsFactory.getInstance(mRilMessageDecoder,
+                mIccFileHandler, mContext);
+        assertEquals(false, cmdPF.processSMSEventNotify(mCommandDetails, mCtlvs));
+    }
+
+    @Test
+    public void testSkipFdnCheckforSTKSmsViaCatService() {
+        CommandParams cmdPrms = new CommandParams(mCommandDetails);
+        when(mProxyController.getSmsController()).thenReturn(mSmsController);
+        mCatService.sendStkSms("test", "12345", 1, cmdPrms, mProxyController);
+        verify(mSmsController, Mockito.times(0)).isNumberBlockedByFDN(1, "12345",
+                "com.android.internal.telephony");
+    }
+
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
similarity index 62%
rename from tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
index 1812fa0..b6d77e9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
@@ -14,23 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.dataconnection;
+package com.android.internal.telephony.data;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
 
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.TelephonyTest;
 
@@ -118,213 +115,7 @@
     }
 
     @Test
-    @SmallTest
-    public void testIsMetered() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
-        doReturn(false).when(mServiceState).getDataRoaming();
-        doReturn(1).when(mPhone).getSubId();
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_SUPL, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_CBS, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DUN, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
-
-        // Carrier config settings changes.
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
-    }
-
-    @Test
-    @SmallTest
-    public void testIsRoamingMetered() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        doReturn(true).when(mServiceState).getDataRoaming();
-        doReturn(1).when(mPhone).getSubId();
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
-
-        // Carrier config settings changes.
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_FOTA_STRING});
-
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
-    }
-
-    @Test
-    @SmallTest
-    public void testIsMeteredAnother() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
-
-        doReturn(false).when(mServiceState).getDataRoaming();
-        doReturn(1).when(mPhone).getSubId();
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_XCAP), mPhone));
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_ENTERPRISE), mPhone));
-    }
-
-    @Test
-    @SmallTest
-    public void testIsRoamingMeteredAnother() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
-        doReturn(true).when(mServiceState).getDataRoaming();
-        doReturn(2).when(mPhone).getSubId();
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
-
-        assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_SUPL, mPhone));
-        assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_CBS, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DUN, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
-        assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
-    }
-
-    @Test
-    @SmallTest
-    public void testIsMeteredNothingCharged() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{});
-
-        doReturn(false).when(mServiceState).getDataRoaming();
-        doReturn(3).when(mPhone).getSubId();
-
-        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-    }
-
-    @Test
-    @SmallTest
-    public void testIsRoamingMeteredNothingCharged() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{});
-        doReturn(true).when(mServiceState).getDataRoaming();
-        doReturn(3).when(mPhone).getSubId();
-
-        assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
-
-        assertFalse(ApnSettingUtils.isMetered(
-                createApnSetting(ApnSetting.TYPE_ALL), mPhone));
-    }
-
-    @Test
-    @SmallTest
     public void testCanHandleType() {
-        String types[] = {"mms"};
-
         assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
                 .canHandleType(ApnSetting.TYPE_MMS));
 
@@ -402,7 +193,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEquals() throws Exception {
         final int dummyInt = 1;
         final int dummyLong = 1;
@@ -451,7 +241,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEqualsRoamingProtocol() {
         ApnSetting apn1 = new ApnSetting.Builder()
                 .setId(1234)
@@ -485,7 +274,6 @@
     }
 
     @Test
-    @SmallTest
     public void testCanHandleNetwork() {
         ApnSetting apn1 = new ApnSetting.Builder()
                 .setId(1234)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
index 511b2df..3773756 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/CellularNetworkValidatorTest.java
@@ -490,6 +490,7 @@
         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         PersistableBundle bundle = carrierConfigManager.getConfigForSubId(anyInt());
-        bundle.putLong(CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, ttl);
+        bundle.putLong(CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG,
+                ttl);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
index 7ee5e23..4b5189a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataCallResponseTest.java
@@ -16,12 +16,6 @@
 
 package com.android.internal.telephony.data;
 
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
 import android.net.InetAddresses;
 import android.net.LinkAddress;
 import android.os.Parcel;
@@ -37,16 +31,21 @@
 import java.util.Arrays;
 
 public class DataCallResponseTest extends AndroidTestCase {
-    public static final String FAKE_DNN = "FAKE_DNN";
-    public static final String FAKE_DNN_2 = "FAKE_DNN_2";
+    private static final String FAKE_ADDRESS = "99.88.77.66";
+    private static final String FAKE_DNS = "55.66.77.88";
+    private static final String FAKE_DNN = "FAKE_DNN";
+    private static final String FAKE_DNN_2 = "FAKE_DNN_2";
+    private static final String FAKE_GATEWAY = "11.22.33.44";
+    private static final String FAKE_IFNAME = "FAKE IFNAME";
+    private static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
     // 97a498e3fc925c9489860333d06e4e470a454e5445525052495345.
     // [OsAppId.ANDROID_OS_ID, "ENTERPRISE", 1]
-    public static final byte[] FAKE_OS_APP_ID = {-105, -92, -104, -29, -4, -110, 92,
+    private static final byte[] FAKE_OS_APP_ID = {-105, -92, -104, -29, -4, -110, 92,
             -108, -119, -122, 3, 51, -48, 110, 78, 71, 10, 69, 78, 84, 69,
             82, 80, 82, 73, 83, 69};
     // 97a498e3fc925c9489860333d06e4e470a454e544552505249534532.
     // [OsAppId.ANDROID_OS_ID, "ENTERPRISE", 2]
-    public static final byte[] FAKE_OS_APP_ID_2 = {-105, -92, -104, -29, -4, -110, 92,
+    private static final byte[] FAKE_OS_APP_ID_2 = {-105, -92, -104, -29, -4, -110, 92,
             -108, -119, -122, 3, 51, -48, 110, 78, 71, 10, 69, 78, 84, 69,
             82, 80, 82, 73, 83, 69, 50};
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java
deleted file mode 100644
index 252274c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataEnabledOverrideTest.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.data;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class DataEnabledOverrideTest extends TelephonyTest {
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    @Test
-    @SmallTest
-    public void testCreateByRules() throws Exception {
-        DataEnabledOverride deo1 = new DataEnabledOverride(
-                "mms=nonDefault, default=inVoiceCall&nonDefault");
-        DataEnabledOverride deo2 = new DataEnabledOverride(
-                "mms=nonDefault, default=inVoiceCall");
-        DataEnabledOverride deo3 = new DataEnabledOverride(
-                "default=inVoiceCall&nonDefault, mms=nonDefault");
-        assertEquals(deo1, deo3);
-        assertNotEquals(deo1, deo2);
-    }
-
-    @Test
-    @SmallTest
-    public void testOverrideEnabled() throws Exception {
-        DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                "mms=nonDefault, default=inVoiceCall&nonDefault");
-        doReturn(1).when(mPhone).getSubId();
-        doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_MMS));
-
-        doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-
-        doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-    }
-
-    @Test
-    @SmallTest
-    public void testGetRules() throws Exception {
-        DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                "mms=nonDefault, default=inVoiceCall&nonDefault");
-        String rules = dataEnabledOverride.getRules();
-        assertEquals(dataEnabledOverride, new DataEnabledOverride(rules));
-    }
-
-    @Test
-    @SmallTest
-    public void testUpdateRules() throws Exception {
-        DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                "mms=nonDefault, default=inVoiceCall&nonDefault");
-        doReturn(1).when(mPhone).getSubId();
-        doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_MMS));
-
-        doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-
-        doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-
-        dataEnabledOverride.updateRules("");
-
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_MMS));
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-    }
-
-    @Test
-    @SmallTest
-    public void testAlwaysEnabled() throws Exception {
-        DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                "mms   =unconditionally,    default=  unconditionally ,   ");
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_MMS));
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-
-        dataEnabledOverride.updateRules("");
-
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_MMS));
-        assertFalse(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-    }
-
-    @Test
-    @SmallTest
-    public void testAllApnTypesInRule() throws Exception {
-        DataEnabledOverride dataEnabledOverride = new DataEnabledOverride("*=inVoiceCall");
-        doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-
-        assertTrue(dataEnabledOverride.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_FOTA));
-    }
-
-    @Test
-    @SmallTest
-    public void testInvalidRules() throws Exception {
-        try {
-            DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                    "default=xyz");
-            fail("Invalid conditions but not threw IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-
-        }
-
-        try {
-            DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                    "mms=");
-            fail("Invalid conditions but not threw IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-
-        }
-
-        try {
-            DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                    "abc=nonDefault");
-            fail("Invalid APN type but not threw IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-
-        }
-
-        try {
-            DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                    " =nonDefault");
-            fail("Invalid APN type but not threw IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-
-        }
-
-        try {
-            DataEnabledOverride dataEnabledOverride = new DataEnabledOverride(
-                    "Invalid rule");
-            fail("Invalid rule but not threw IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testSetAlwaysAllowMms() throws Exception {
-        DataEnabledOverride deo = new DataEnabledOverride("");
-        deo.setAlwaysAllowMms(true);
-        assertTrue(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
-        deo.setAlwaysAllowMms(false);
-        assertFalse(deo.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_MMS));
-    }
-
-    @Test
-    @SmallTest
-    public void testSetDataAllowedInVoiceCall() throws Exception {
-        DataEnabledOverride deo = new DataEnabledOverride("");
-        deo.setDataAllowedInVoiceCall(true);
-        assertFalse(deo.getRules(), deo.shouldOverrideDataEnabledSettings(mPhone,
-                ApnSetting.TYPE_DEFAULT));
-        assertTrue(deo.isDataAllowedInVoiceCall());
-        doReturn(1).when(mPhone).getSubId();
-        doReturn(2).when(mSubscriptionController).getDefaultSmsSubId();
-
-        doReturn(PhoneConstants.State.OFFHOOK).when(mPhone).getState();
-        deo.setDataAllowedInVoiceCall(false);
-        assertFalse(deo.getRules(), deo.shouldOverrideDataEnabledSettings(
-                mPhone, ApnSetting.TYPE_DEFAULT));
-        assertFalse(deo.isDataAllowedInVoiceCall());
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index d5abf0f..a967539 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.data;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import static com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import static com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 
@@ -103,6 +105,7 @@
 import com.android.internal.telephony.MultiSimSettingController;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
@@ -712,8 +715,8 @@
         doReturn(true).when(mSST).getPowerStateFromCarrier();
         doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
         doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
-        doReturn("").when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
-        doReturn(true).when(mSubscriptionController).setDataEnabledOverrideRules(
+        doReturn("").when(mSubscriptionController).getEnabledMobileDataPolicies(anyInt());
+        doReturn(true).when(mSubscriptionController).setEnabledMobileDataPolicies(
                 anyInt(), anyString());
 
         List<SubscriptionInfo> infoList = new ArrayList<>();
@@ -721,7 +724,8 @@
         doReturn(infoList).when(mSubscriptionController).getSubscriptionsInGroup(
                 any(), any(), any());
         doReturn(true).when(mSubscriptionController).isActiveSubId(anyInt());
-        doReturn(0).when(mSubscriptionController).getPhoneId(anyInt());
+        doReturn(0).when(mSubscriptionController).getPhoneId(1);
+        doReturn(1).when(mSubscriptionController).getPhoneId(2);
 
         for (int transport : new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                 AccessNetworkConstants.TRANSPORT_TYPE_WLAN}) {
@@ -1577,7 +1581,8 @@
         mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
                 TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
         // Always allow MMS
-        mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(true);
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true);
         processAllMessages();
         mDataNetworkControllerUT.addNetworkRequest(
                 createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
@@ -1590,7 +1595,8 @@
         verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
 
         // Remove MMS data enabled override
-        mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(false);
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, false);
         processAllMessages();
 
         // Make sure MMS is torn down when the override is disabled.
@@ -1608,7 +1614,8 @@
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
         // Always allow MMS
-        mDataNetworkControllerUT.getDataSettingsManager().setAlwaysAllowMmsData(true);
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true);
         processAllMessages();
 
         mDataNetworkControllerUT.addNetworkRequest(
@@ -1621,6 +1628,89 @@
     }
 
     @Test
+    public void testIsDataEnabledOverriddenForApnDataDuringCall() throws Exception {
+        doReturn(1).when(mPhone).getSubId();
+        doReturn(2).when(mSubscriptionController).getDefaultDataSubId();
+        // Data disabled
+        mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+
+        // Enable during data call mobile policy
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, true);
+        processAllMessages();
+
+        // No active phone call
+        doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Verify no internet connection due to no active phone call
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+        // Phone ringing
+        doReturn(PhoneConstants.State.RINGING).when(mPhone).getState();
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Verify internet connection
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+        // Disable during data call mobile policy
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, false);
+        processAllMessages();
+
+        // Verify no internet connection
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+    }
+
+    @Test
+    public void testIsDataEnabledOverriddenForApnAutoDataSwitch() throws Exception {
+        // Assume phone2 is the default data phone
+        Phone phone2 = Mockito.mock(Phone.class);
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, phone2});
+        doReturn(2).when(mSubscriptionController).getDefaultDataSubId();
+
+        // Data disabled on nonDDS
+        mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+                TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+
+        // Enable auto data switch mobile policy
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
+        processAllMessages();
+
+        // use disabled data on DDS
+        doReturn(false).when(phone2).isUserDataEnabled();
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Verify no internet connection
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+        // use enabled data on DDS
+        doReturn(true).when(phone2).isUserDataEnabled();
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+
+        // Verify internet connection
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+        // Disable auto data switch mobile policy
+        mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+                .MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, false);
+        processAllMessages();
+
+        // Verify no internet connection
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+    }
+
+    @Test
     public void testUnmeteredRequestPreferredOnIwlan() throws Exception {
         // Preferred on cellular
         doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
@@ -3751,7 +3841,7 @@
         }).when(mMockedWwanDataServiceManager).deactivateDataCall(
                 anyInt(), anyInt(), any(Message.class));
         // Simulate old devices
-        doReturn(RIL.RADIO_HAL_VERSION_1_6).when(mPhone).getHalVersion();
+        doReturn(RIL.RADIO_HAL_VERSION_1_6).when(mPhone).getHalVersion(HAL_SERVICE_DATA);
 
         testSetupDataNetwork();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 7bab74d..9acde63 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -989,12 +989,30 @@
     public void testHandover() throws Exception {
         setupDataNetwork();
 
-        setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager, 456);
         // Now handover to IWLAN
         mDataNetworkUT.startHandover(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, null);
+        // the source transport might report PDN lost
+        mDataNetworkUT.sendMessage(8/*EVENT_DATA_STATE_CHANGED*/,
+                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                        Collections.emptyList(), null));
         processAllMessages();
 
+        // make sure interface name of source PDN is cleared
+        assertThat(mDataNetworkUT.getLinkProperties().getInterfaceName()).isNotEqualTo("ifname");
+        // make sure the capability of source PDN is set to SUSPENDED
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)).isFalse();
         verify(mMockedWwanDataServiceManager).startHandover(eq(123), any(Message.class));
+
+        // continue the HO
+        setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager, 456);
+        Message msg = new Message();
+        msg.what = 26/*EVENT_NOTIFY_HANDOVER_STARTED_RESPONSE*/;
+        msg.arg2 = AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+        msg.obj = null;
+        mDataNetworkUT.sendMessage(msg);
+        processAllMessages();
+
         verify(mLinkBandwidthEstimator).unregisterCallback(any(
                 LinkBandwidthEstimatorCallback.class));
         assertThat(mDataNetworkUT.getTransport())
@@ -1044,8 +1062,6 @@
         verify(mDataNetworkCallback).onHandoverFailed(eq(mDataNetworkUT),
                 eq(DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE), eq(-1L),
                 eq(DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN));
-        verify(mLinkBandwidthEstimator, never()).unregisterForBandwidthChanged(
-                eq(mDataNetworkUT.getHandler()));
         assertThat(mDataNetworkUT.getTransport())
                 .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         assertThat(mDataNetworkUT.getId()).isEqualTo(123);
@@ -1133,7 +1149,7 @@
         verify(mVcnManager).addVcnNetworkPolicyChangeListener(any(Executor.class),
                 any(VcnNetworkPolicyChangeListener.class));
         verify(mSST).registerForCssIndicatorChanged(any(Handler.class), anyInt(), eq(null));
-        verify(mSST).registerForServiceStateChanged(any(Handler.class), anyInt());
+        verify(mSST).registerForServiceStateChanged(any(Handler.class), anyInt(), any());
         verify(mCT).registerForVoiceCallStarted(any(Handler.class), anyInt(), eq(null));
         verify(mCT).registerForVoiceCallEnded(any(Handler.class), anyInt(), eq(null));
         verify(mImsCT).registerForVoiceCallStarted(any(Handler.class), anyInt(), eq(null));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index 93dc75d..f8de475 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -931,6 +931,8 @@
                         .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
                         .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
                         .setCarrierEnabled(true)
+                        .setMvnoMatchData("1")
+                        .setMvnoType(1)
                         .build())
                 .build();
 
@@ -945,6 +947,8 @@
                         .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
                         .setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
                         .setCarrierEnabled(true)
+                        .setMvnoMatchData("2")
+                        .setMvnoType(2)
                         .build())
                 .build();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
new file mode 100644
index 0000000..0014a1b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DataSettingsManagerTest extends TelephonyTest {
+
+    // Mocked
+    DataSettingsManagerCallback mMockedDataSettingsManagerCallback;
+
+    DataSettingsManager mDataSettingsManagerUT;
+
+    @Before
+    public void setUp() throws Exception {
+        logd("DataSettingsManagerTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        mMockedDataSettingsManagerCallback = Mockito.mock(DataSettingsManagerCallback.class);
+
+        doReturn("").when(mSubscriptionController).getEnabledMobileDataPolicies(anyInt());
+        doReturn(true).when(mSubscriptionController).setEnabledMobileDataPolicies(
+                anyInt(), anyString());
+
+        mDataSettingsManagerUT = new DataSettingsManager(mPhone, mDataNetworkController,
+                Looper.myLooper(), mMockedDataSettingsManagerCallback);
+        logd("DataSettingsManagerTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        logd("tearDown");
+        super.tearDown();
+    }
+
+    @Test
+    public void testMobileDataPolicyParsing() {
+        //Valid new data policy
+        Set<Integer> policies = mDataSettingsManagerUT.getMobileDataPolicyEnabled("1, 2");
+        assertThat(policies.size()).isEqualTo(2);
+        Set<Integer> policies2 = mDataSettingsManagerUT.getMobileDataPolicyEnabled(",2");
+        assertThat(policies2.size()).isEqualTo(1);
+        Set<Integer> policies3 = mDataSettingsManagerUT.getMobileDataPolicyEnabled("");
+        assertThat(policies3.size()).isEqualTo(0);
+
+        // Invalid
+        Set<Integer> invalid = mDataSettingsManagerUT.getMobileDataPolicyEnabled(
+                "nonExistent, 1, 2");
+        assertThat(invalid.size()).isEqualTo(2);
+
+        Set<Integer> invalid2 = mDataSettingsManagerUT.getMobileDataPolicyEnabled(
+                "nonExistent ,,");
+        assertThat(invalid2.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetPolicies() {
+        mDataSettingsManagerUT.setMobileDataPolicy(1, true);
+        mDataSettingsManagerUT.setMobileDataPolicy(2, true);
+        processAllMessages();
+
+        ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
+        verify(mSubscriptionController, times(2))
+                .setEnabledMobileDataPolicies(anyInt(), stringArgumentCaptor.capture());
+        assertEquals("1,2", stringArgumentCaptor.getValue());
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index de0998d..a8290af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -78,7 +78,7 @@
                 })
                 .when(mDataStallRecoveryManagerCallback)
                 .invokeFromExecutor(any(Runnable.class));
-        doReturn("").when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
+        doReturn("").when(mSubscriptionController).getEnabledMobileDataPolicies(anyInt());
 
         mDataStallRecoveryManager =
                 new DataStallRecoveryManager(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
index ba001f2..d41be7d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/LinkBandwidthEstimatorTest.java
@@ -32,7 +32,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
@@ -43,9 +42,7 @@
 import static org.mockito.Mockito.when;
 
 import android.net.NetworkCapabilities;
-import android.os.AsyncResult;
 import android.os.Handler;
-import android.os.Message;
 import android.telephony.CellIdentityLte;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NetworkRegistrationInfo;
@@ -63,7 +60,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
@@ -80,7 +76,6 @@
             new ModemActivityInfo(100L, 0, 0, TX_TIME_2_MS, RX_TIME_2_MS);
     private static final ModemActivityInfo MAI_RX_TIME_HIGH =
             new ModemActivityInfo(100L, 0, 0, TX_TIME_1_MS, RX_TIME_2_MS);
-    private static final int EVENT_BANDWIDTH_ESTIMATOR_UPDATE = 1;
     private NetworkCapabilities mNetworkCapabilities;
     private CellIdentityLte mCellIdentity;
     private long mElapsedTimeMs = 0;
@@ -89,14 +84,20 @@
     private NetworkRegistrationInfo mNri;
 
     // Mocked classes
-    TelephonyFacade mTelephonyFacade;
+    private TelephonyFacade mTelephonyFacade;
     private Handler mTestHandler;
+    private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mTelephonyFacade = mock(TelephonyFacade.class);
         mTestHandler = mock(Handler.class);
+        mLinkBandwidthEstimatorCallback = Mockito.mock(LinkBandwidthEstimatorCallback.class);
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArguments()[0]).run();
+            return null;
+        }).when(mLinkBandwidthEstimatorCallback).invokeFromExecutor(any(Runnable.class));
         mNetworkCapabilities = new NetworkCapabilities.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
@@ -116,10 +117,10 @@
         when(mSignalStrength.getDbm()).thenReturn(-100);
         when(mSignalStrength.getLevel()).thenReturn(1);
         mLBE = new LinkBandwidthEstimator(mPhone, mTelephonyFacade);
-        mLBE.registerForBandwidthChanged(mTestHandler, EVENT_BANDWIDTH_ESTIMATOR_UPDATE, null);
         mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
         mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, false).sendToTarget();
         mLBE.obtainMessage(MSG_ACTIVE_PHONE_CHANGED, 1).sendToTarget();
+        mLBE.registerCallback(mLinkBandwidthEstimatorCallback);
         processAllMessages();
     }
 
@@ -184,12 +185,8 @@
     }
 
     private void verifyUpdateBandwidth(int txKbps, int rxKbps) {
-        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mTestHandler, atLeast(1))
-                .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_BANDWIDTH_ESTIMATOR_UPDATE, messageArgumentCaptor.getValue().what);
-        assertEquals(new Pair<Integer, Integer>(txKbps, rxKbps),
-                ((AsyncResult) messageArgumentCaptor.getValue().obj).result);
+        verify(mLinkBandwidthEstimatorCallback, atLeast(1))
+                .onBandwidthChanged(eq(txKbps), eq(rxKbps));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index d50cb72..bd4b5f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -28,10 +28,6 @@
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
 
 import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_IMS_RADIO_TECH_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_MULTI_SIM_CONFIG_CHANGED;
-import static com.android.internal.telephony.data.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -62,10 +58,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
+import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -77,9 +75,9 @@
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 
 import org.junit.After;
 import org.junit.Before;
@@ -87,24 +85,32 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
+import java.lang.reflect.Field;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.LinkedBlockingQueue;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class PhoneSwitcherTest extends TelephonyTest {
+    private static final int AUTO_DATA_SWITCH_NOTIFICATION = 1;
     private static final int ACTIVE_PHONE_SWITCH = 1;
     private static final int EVENT_RADIO_ON = 108;
     private static final int EVENT_MODEM_COMMAND_DONE = 112;
+    private static final int EVENT_EVALUATE_AUTO_SWITCH = 111;
+    private static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
+    private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117;
+    private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109;
+    private static final int EVENT_SERVICE_STATE_CHANGED = 114;
 
     // Mocked classes
     CompletableFuture<Boolean> mFuturePhone;
     private CommandsInterface mCommandsInterface0;
     private CommandsInterface mCommandsInterface1;
     private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+    private ServiceStateTracker mSST2;
     private Phone mImsPhone;
-    // TODO: Add logic for DataSettingsManager
-    private DataEnabledSettings mDataEnabledSettings2;
+    private DataSettingsManager mDataSettingsManager2;
     private Handler mActivePhoneSwitchHandler;
     private GsmCdmaCall mActiveCall;
     private GsmCdmaCall mHoldingCall;
@@ -116,11 +122,13 @@
     PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
     private SubscriptionInfo mSubscriptionInfo;
 
-    private PhoneSwitcher mPhoneSwitcher;
+    private PhoneSwitcher mPhoneSwitcherUT;
     private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
     private ConnectivityManager mConnectivityManager;
     // The messenger of PhoneSwitcher used to receive network requests.
     private Messenger mNetworkProviderMessenger = null;
+    private Map<Integer, DataSettingsManager.DataSettingsManagerCallback>
+            mDataSettingsManagerCallbacks;
     private int mDefaultDataSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private int[][] mSlotIndexToSubId;
     private boolean[] mDataAllowed;
@@ -135,8 +143,9 @@
         mCommandsInterface0 = mock(CommandsInterface.class);
         mCommandsInterface1 = mock(CommandsInterface.class);
         mPhone2 = mock(Phone.class); // mPhone as phone 1 is already defined in TelephonyTest.
+        mSST2 = mock(ServiceStateTracker.class);
         mImsPhone = mock(Phone.class);
-        mDataEnabledSettings2 = mock(DataEnabledSettings.class);
+        mDataSettingsManager2 = mock(DataSettingsManager.class);
         mActivePhoneSwitchHandler = mock(Handler.class);
         mActiveCall = mock(GsmCdmaCall.class);
         mHoldingCall = mock(GsmCdmaCall.class);
@@ -167,7 +176,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mPhoneSwitcher = null;
+        mPhoneSwitcherUT = null;
         mSubChangedListener = null;
         mConnectivityManager = null;
         mNetworkProviderMessenger = null;
@@ -188,14 +197,14 @@
 
         NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
 
-        assertFalse("phone active after request", mPhoneSwitcher
+        assertFalse("phone active after request", mPhoneSwitcherUT
                 .shouldApplyNetworkRequest(
                         new TelephonyNetworkRequest(internetNetworkRequest, mPhone), 0));
 
         // not registered yet - shouldn't inc
         verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
 
-        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+        mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
@@ -210,7 +219,7 @@
         processAllMessages();
 
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -231,7 +240,7 @@
         // 1 lose default via default sub change
         setDefaultDataSubId(1);
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -241,7 +250,7 @@
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -251,7 +260,7 @@
         // 2 gain default via default sub change
         setDefaultDataSubId(0);
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -262,7 +271,7 @@
         setSlotIndexToSubId(0, 2);
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
@@ -275,7 +284,7 @@
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -285,7 +294,7 @@
         // 5 lose default network request
         releaseNetworkRequest(internetNetworkRequest);
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -295,7 +304,7 @@
         // 6 gain subscription-specific request
         NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -307,7 +316,7 @@
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -319,7 +328,7 @@
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -329,7 +338,7 @@
         // 9 lose subscription-specific request
         releaseNetworkRequest(specificInternetRequest);
 
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mActivePhoneSwitchHandler);
@@ -357,6 +366,254 @@
 //        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
     }
 
+    /** Test Data Auto Switch **/
+
+    /**
+     * Trigger conditions
+     * 1. service state changes
+     * 2. data setting changes
+     *      - user toggle data
+     *      - user toggle auto switch feature
+     * 3. default network changes
+     *      - current network lost
+     *      - network become active on non-cellular network
+     * 4. subscription changes
+     *      - slot/sub mapping changes
+     */
+    @Test
+    @SmallTest
+    public void testAutoDataSwitchCancelScenario_onPrimary() throws Exception {
+        initialize();
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        // 0. When all conditions met
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+
+        // Verify attempting to switch
+        verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        doReturn(true).when(mCellularNetworkValidator).isValidating();
+
+        // 1. Service state becomes not ideal - primary is available again
+        clearInvocations(mCellularNetworkValidator);
+        serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator).stopValidation();
+
+        // 2.1 User data disabled on primary SIM
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(false).when(mPhone).isUserDataEnabled();
+        mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator).stopValidation();
+
+        // 2.2 Auto switch feature is disabled
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(false).when(mPhone2).isDataAllowed();
+        mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(false, 123 , "");
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator).stopValidation();
+
+        // 3.1 No default network
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
+                .when(mConnectivityManager).getNetworkCapabilities(any());
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator).stopValidation();
+    }
+
+    public void testAutoSwitchToSecondarySucceed() {
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
+        processAllMessages();
+        // Confirm auto switched to secondary sub Id 1/phone 0
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+    }
+
+    @Test
+    @SmallTest
+    public void testAutoDataSwitch_switchBackToPrimary() throws Exception {
+        initialize();
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        testAutoSwitchToSecondarySucceed();
+        // 1.1 service state changes - primary becomes available, need validation pass to switch
+        serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+        verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+        processAllMessages();
+
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId()); // since validation failed
+
+        serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        processAllFutureMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
+        processAllMessages();
+
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since validation passed
+
+        testAutoSwitchToSecondarySucceed();
+        // 1.2 service state changes - secondary becomes unavailable, NO need validation
+        // The later validation requirement overrides the previous
+        serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
+        serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
+        processAllFutureMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+        processAllMessages();
+
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+
+        testAutoSwitchToSecondarySucceed();
+        // 2.1 User data disabled on primary SIM
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(false).when(mPhone).isUserDataEnabled();
+        mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+        processAllFutureMessages();
+
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+        verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+
+        testAutoSwitchToSecondarySucceed();
+        // 2.2 Auto switch feature is disabled
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(false).when(mPhone2).isDataAllowed();
+        mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
+        processAllFutureMessages();
+
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+        verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+
+        testAutoSwitchToSecondarySucceed();
+        // 3.1 Default network is active on non-cellular transport
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
+                .when(mConnectivityManager).getNetworkCapabilities(any());
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+        processAllFutureMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+        processAllMessages();
+
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+    }
+
+    @Test
+    @SmallTest
+    public void testAutoDataSwitchCancel_onSecondary() throws Exception {
+        initialize();
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        testAutoSwitchToSecondarySucceed();
+        clearInvocations(mCellularNetworkValidator);
+        // attempts the switch back due to secondary becomes ROAMING
+        serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        doReturn(true).when(mCellularNetworkValidator).isValidating();
+
+        // cancel the switch back attempt due to secondary back to HOME
+        serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        processAllMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
+        processAllMessages();
+
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+        verify(mCellularNetworkValidator).stopValidation();
+    }
+
+    @Test
+    @SmallTest
+    public void testAutoDataSwitchRetry() throws Exception {
+        initialize();
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+
+        // Verify attempting to switch
+        verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
+        processAllMessages();
+
+        assertTrue(mPhoneSwitcherUT.hasMessages(EVENT_EVALUATE_AUTO_SWITCH));
+
+        processAllFutureMessages();
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
+        processAllFutureMessages();
+
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+    }
+
+    @Test
+    @SmallTest
+    public void testAutoDataSwitchSetNotification() throws Exception {
+        SubscriptionInfo mockedInfo = mock(SubscriptionInfo.class);
+        doReturn(false).when(mockedInfo).isOpportunistic();
+        doReturn(mockedInfo).when(mSubscriptionController).getSubscriptionInfo(anyInt());
+        initialize();
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        testAutoSwitchToSecondarySucceed();
+        clearInvocations(mSubscriptionController);
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null,  null))
+                .sendToTarget();
+        processAllMessages();
+        verify(mSubscriptionController).getSubscriptionInfo(2);
+
+        // switch back to primary
+        clearInvocations(mSubscriptionController);
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(0, null,  null))
+                .sendToTarget();
+        processAllMessages();
+        verify(mSubscriptionController, never()).getSubscriptionInfo(1);
+
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null,  null))
+                .sendToTarget();
+        processAllMessages();
+        verify(mSubscriptionController, never()).getSubscriptionInfo(2);
+    }
+
     /**
      * Test a multi-sim case with limited active phones:
      * - lose default via default sub change
@@ -382,7 +639,7 @@
         setSlotIndexToSubId(0, 0);
         setSlotIndexToSubId(1, 1);
         setDefaultDataSubId(0);
-        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+        mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
         processAllMessages();
         // verify initial conditions
@@ -395,7 +652,7 @@
         addMmsNetworkRequest(1);
 
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
         // After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
@@ -461,31 +718,114 @@
         assertTrue(mDataAllowed[0]);
 
         // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
         processAllMessages();
         assertFalse(mDataAllowed[0]);
         assertTrue(mDataAllowed[1]);
 
         // Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
         processAllMessages();
         assertTrue(mDataAllowed[0]);
         assertFalse(mDataAllowed[1]);
     }
 
+    /**
+     * TestSetPreferredData in the event of different priorities.
+     * The following events can set preferred data subId with priority in the order of
+     * 1. Emergency call
+     * 2. Voice call (when data during call feature is enabled).
+     * 3. CBRS requests
+     * 4. Auto switch requests
+     */
+    @Test
+    @SmallTest
+    public void testSetPreferredDataCasePriority_CbrsWaitsForVoiceCall() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        // Notify phoneSwitcher about default data sub and default network request.
+        NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
+        // Phone 0 (sub 1) should be activated as it has default data sub.
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+
+        // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
+        processAllMessages();
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
+        // A higher priority event occurring E.g. Phone1 has active IMS call on LTE.
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        doReturn(true).when(mPhone).isDataAllowed();
+        mockImsRegTech(0, REGISTRATION_TECH_LTE);
+        notifyPhoneAsInCall(mPhone);
+
+        // switch shouldn't occur due to the higher priority event
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+        assertEquals(2, mPhoneSwitcherUT.getAutoSelectedDataSubId());
+
+        // The higher priority event ends, time to switch to auto selected subId.
+        notifyPhoneAsInactive(mPhone);
+
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+        assertEquals(2, mPhoneSwitcherUT.getAutoSelectedDataSubId());
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+    }
+
+    @Test
+    @SmallTest
+    public void testSetPreferredData_NoAutoSwitchWhenCbrs() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        clearInvocations(mCellularNetworkValidator);
+        doReturn(new int[1]).when(mSubscriptionController).getActiveSubIdList(true);
+        prepareIdealAutoSwitchCondition();
+        processAllFutureMessages();
+
+        verify(mCellularNetworkValidator, never()).validate(eq(2), anyLong(), eq(false),
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId());
+    }
+
     @Test
     @SmallTest
     public void testSetPreferredDataModemCommand() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
-        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+        mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
-        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+        mPhoneSwitcherUT.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mMockRadioConfig);
@@ -500,7 +840,7 @@
         // Phone 0 (sub 1) should be preferred data phone as it has default data sub.
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
         clearInvocations(mMockRadioConfig);
@@ -512,55 +852,55 @@
         NetworkRequest mmsRequest = addMmsNetworkRequest(2);
         verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
         verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
 
         // Set sub 2 as preferred sub should make phone 1 preferredDataModem
         doReturn(true).when(mSubscriptionController).isOpportunistic(2);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
 
         clearInvocations(mMockRadioConfig);
         clearInvocations(mActivePhoneSwitchHandler);
 
         // Unset preferred sub should make phone0 preferredDataModem again.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
         processAllMessages();
 
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(mmsRequest, mPhone), 1));
 
         // SetDataAllowed should never be triggered.
@@ -569,7 +909,7 @@
 
         // Set preferred data modem should be triggered after radio on or available.
         clearInvocations(mMockRadioConfig);
-        Message.obtain(mPhoneSwitcher, EVENT_RADIO_ON, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_RADIO_ON, res).sendToTarget();
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
     }
@@ -590,42 +930,42 @@
         setDefaultDataSubId(1);
 
         // Phone 0 (sub 1) should be activated as it has default data sub.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, null);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
+                eq(mPhoneSwitcherUT.mValidationCallback));
         // Validation failed. Preferred data sub should remain 1, data phone should remain 0.
-        mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
         processAllMessages();
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Validation succeeds. Preferred data sub changes to 2, data phone changes to 1.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
         processAllMessages();
-        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Switching data back to primary (subId 1) with customized validation timeout.
         long timeout = 1234;
         mContextFixture.getCarrierConfigBundle().putLong(
                 KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, timeout);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, null);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(1), eq(timeout), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
-        mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
+                eq(mPhoneSwitcherUT.mValidationCallback));
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
         processAllMessages();
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
 
     private void mockImsRegTech(int phoneId, int regTech) {
         doReturn(regTech).when(mMockImsRegTechProvider).get(any(), eq(phoneId));
-        mPhoneSwitcher.mImsRegTechProvider = mMockImsRegTechProvider;
+        mPhoneSwitcherUT.mImsRegTechProvider = mMockImsRegTechProvider;
     }
 
     @Test
@@ -643,17 +983,24 @@
         processAllMessages();
 
         // Phone 0 should be the default data phoneId.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
         // trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        doReturn(true).when(mPhone2).isDataAllowed();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInCall(mImsPhone);
 
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+        // User turns on data on Phone 0
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        notifyDataEnabled(true);
+
         // Phone 1 should become the default data phone.
-        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
 
     @Test
@@ -672,17 +1019,24 @@
         processAllMessages();
 
         // Phone 0 should be the default data phoneId.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
         // trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        doReturn(true).when(mPhone2).isDataAllowed();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInDial(mImsPhone);
 
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+        // User turns on data on Phone 0
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        notifyDataEnabled(true);
+
         // Phone 1 should become the default data phone.
-        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
     @Test
     @SmallTest
@@ -700,17 +1054,24 @@
         processAllMessages();
 
         // Phone 0 should be the default data phoneId.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
         // trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        doReturn(true).when(mPhone2).isDataAllowed();
         mockImsRegTech(1, REGISTRATION_TECH_LTE);
         notifyPhoneAsInIncomingCall(mImsPhone);
 
+        // Phone 0 should be the default data phoneId.
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+        // User turns on data on Phone 0
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        notifyDataEnabled(true);
+
         // Phone 1 should become the default data phone.
-        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
 
     @Test
@@ -728,16 +1089,16 @@
         processAllMessages();
 
         // Phone 0 should be the default data phoneId.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone2 has active call, but data is turned off. So no data switching should happen.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        doReturn(true).when(mPhone2).isDataAllowed();
         mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
         notifyPhoneAsInCall(mImsPhone);
 
         // Phone 0 should remain the default data phone.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
 
     @Test
@@ -755,17 +1116,18 @@
         processAllMessages();
 
         // Phone 0 should be the default data phoneId.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone 1 has active IMS call on CROSS_SIM. And data of DEFAULT apn is enabled. This should
         // not trigger data switch.
         doReturn(mImsPhone).when(mPhone2).getImsPhone();
-        doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        doReturn(true).when(mPhone).isUserDataEnabled();
+        doReturn(true).when(mPhone2).isDataAllowed();
         mockImsRegTech(1, REGISTRATION_TECH_CROSS_SIM);
         notifyPhoneAsInCall(mImsPhone);
 
         // Phone 0 should remain the default data phone.
-        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
 
         // Phone 1 has has handed over the call to LTE. And data of DEFAULT apn is enabled.
         // This should trigger data switch.
@@ -773,7 +1135,7 @@
         notifyImsRegistrationTechChange(mPhone2);
 
         // Phone 1 should become the default data phone.
-        assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+        assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
 
     @Test
@@ -788,9 +1150,9 @@
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
         clearInvocations(mMockRadioConfig);
         setAllPhonesInactive();
@@ -800,35 +1162,36 @@
         notifyDataEnabled(false);
         notifyPhoneAsInCall(mPhone2);
         verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
 
         // Phone2 has active call. So data switch to it.
+        doReturn(true).when(mPhone).isUserDataEnabled();
         notifyDataEnabled(true);
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
         clearInvocations(mMockRadioConfig);
 
         // Phone2 call ended. So data switch back to default data sub.
         notifyPhoneAsInactive(mPhone2);
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
         clearInvocations(mMockRadioConfig);
 
         // Phone2 has holding call, but data is turned off. So no data switching should happen.
         notifyPhoneAsInHoldingCall(mPhone2);
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
     }
 
@@ -845,16 +1208,16 @@
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
         NetworkRequest internetRequest = addInternetNetworkRequest(2, 50);
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
 
         // Restricted network request will should be applied.
         internetRequest = addInternetNetworkRequest(2, 50, true);
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
     }
 
@@ -870,7 +1233,7 @@
         clearInvocations(mMockRadioConfig);
 
         // override the phone ID in prep for emergency call
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
         processAllMessages();
         verify(mFuturePhone).complete(true);
@@ -891,7 +1254,7 @@
         clearInvocations(mMockRadioConfig);
 
         // override the phone ID in prep for emergency call
-        mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(0, 1, mFuturePhone);
         processAllMessages();
         // The radio command should never be called because the DDS hasn't changed.
         verify(mMockRadioConfig, never()).setPreferredDataModem(eq(0), any());
@@ -915,7 +1278,7 @@
 
         // override the phone ID in prep for emergency call
 
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
         processAllMessages();
         verify(mFuturePhone).complete(true);
@@ -930,7 +1293,7 @@
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
         // Make sure the correct broadcast is sent out for the phone ID
@@ -952,7 +1315,7 @@
         clearInvocations(mTelephonyRegistryManager);
 
         // override the phone ID in prep for emergency call
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
         processAllMessages();
         verify(mFuturePhone).complete(true);
@@ -981,7 +1344,7 @@
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         // Make sure the correct broadcast is sent out for the phone ID
         verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
@@ -1002,7 +1365,7 @@
         clearInvocations(mTelephonyRegistryManager);
 
         // override the phone ID in prep for emergency call
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, mFuturePhone);
         sendPreferredDataSuccessResult(1);
         processAllMessages();
         verify(mFuturePhone).complete(true);
@@ -1012,7 +1375,7 @@
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
         // Make sure the correct broadcast is sent out for the phone ID
@@ -1037,7 +1400,7 @@
         LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
         CompletableFuture<Boolean> futurePhone = new CompletableFuture<>();
         futurePhone.whenComplete((r, error) -> queue.offer(r));
-        mPhoneSwitcher.overrideDefaultDataForEmergency(1, 1, futurePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(1, 1, futurePhone);
         sendPreferredDataSuccessResult(1);
         processAllMessages();
         Boolean result = queue.poll();
@@ -1047,7 +1410,7 @@
         // try override the phone ID again while there is an existing override for a different phone
         futurePhone = new CompletableFuture<>();
         futurePhone.whenComplete((r, error) -> queue.offer(r));
-        mPhoneSwitcher.overrideDefaultDataForEmergency(0, 1, futurePhone);
+        mPhoneSwitcherUT.overrideDefaultDataForEmergency(0, 1, futurePhone);
         processAllMessages();
         result = queue.poll();
         assertNotNull(result);
@@ -1063,7 +1426,7 @@
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
         AsyncResult res = new AsyncResult(1, null,  null);
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
         // Make sure the correct broadcast is sent out for the phone ID
@@ -1083,50 +1446,65 @@
         // Both are active subscriptions are active sub, as they are in both active slots.
         setSlotIndexToSubId(0, 1);
         setSlotIndexToSubId(1, 2);
-        setDefaultDataSubId(1);
 
+        // Switch to primary before a primary is selected/inactive.
+        setDefaultDataSubId(-1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
+        processAllMessages();
+
+        assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                mPhoneSwitcherUT.getAutoSelectedDataSubId());
+        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+
+        // once the primary is selected, it becomes the active sub.
+        setDefaultDataSubId(2);
+        assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
+
+        setDefaultDataSubId(1);
         // Validating on sub 10 which is inactive.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
+        clearInvocations(mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
 
         // Switch to active subId without validating. Should always succeed.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         // Validating on sub 1 and fails.
         clearInvocations(mSetOpptDataCallback1);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
 
         // Validating on sub 2 and succeeds.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
         processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
         // Switching data back to primary and validation fails.
         clearInvocations(mSetOpptDataCallback2);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
 
         // Switching data back to primary and succeeds.
         clearInvocations(mSetOpptDataCallback2);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(true, 1);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
         processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
@@ -1134,36 +1512,36 @@
         clearInvocations(mSetOpptDataCallback1);
         clearInvocations(mSetOpptDataCallback2);
         clearInvocations(mCellularNetworkValidator);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
+                eq(mPhoneSwitcherUT.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
         verify(mSetOpptDataCallback2, never()).onComplete(anyInt());
         // Validation succeeds.
         doReturn(false).when(mCellularNetworkValidator).isValidating();
-        mPhoneSwitcher.mValidationCallback.onValidationDone(true, 2);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
         processAllMessages();
         verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
 
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
         processAllMessages();
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
         processAllMessages();
         clearInvocations(mSetOpptDataCallback1);
         clearInvocations(mSetOpptDataCallback2);
         clearInvocations(mCellularNetworkValidator);
         // Back to back call, call 1 to switch to subId 2, call 2 to switch back.
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
+                eq(mPhoneSwitcherUT.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
         processAllMessages();
         // Call 1 should be cancelled and failed. Call 2 return success immediately as there's no
@@ -1188,12 +1566,11 @@
 
         setNumPhones(2, 2);
         AsyncResult result = new AsyncResult(null, 2, null);
-        Message.obtain(mPhoneSwitcher, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
         processAllMessages();
 
         verify(mPhone2).registerForEmergencyCallToggle(any(), anyInt(), any());
         verify(mPhone2).registerForPreciseCallStateChanged(any(), anyInt(), any());
-        verify(mDataEnabledSettings2).registerForDataEnabledChanged(any(), anyInt(), any());
 
         clearInvocations(mMockRadioConfig);
         setSlotIndexToSubId(1, 2);
@@ -1213,28 +1590,28 @@
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
         clearInvocations(mMockRadioConfig);
         setAllPhonesInactive();
         // Initialization done.
 
         doReturn(true).when(mSubscriptionController).isOpportunistic(2);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
+                eq(mPhoneSwitcherUT.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
 
         // Network available on different sub. Should do nothing.
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 1);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 1);
         processAllMessages();
         verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
 
         // Network available on corresponding sub. Should confirm switch.
-        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
     }
@@ -1251,28 +1628,28 @@
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
         NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
-        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 0));
-        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+        assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                 new TelephonyNetworkRequest(internetRequest, mPhone), 1));
         clearInvocations(mMockRadioConfig);
         setAllPhonesInactive();
         // Initialization done.
 
         doReturn(true).when(mSubscriptionController).isOpportunistic(2);
-        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
         processAllMessages();
         verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
-                eq(mPhoneSwitcher.mValidationCallback));
+                eq(mPhoneSwitcherUT.mValidationCallback));
         doReturn(true).when(mCellularNetworkValidator).isValidating();
 
         // Validation failed on different sub. Should do nothing.
-        mPhoneSwitcher.mValidationCallback.onValidationDone(false, 1);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
         processAllMessages();
         verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
 
         // Network available on corresponding sub. Should confirm switch.
-        mPhoneSwitcher.mValidationCallback.onValidationDone(false, 2);
+        mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
         processAllMessages();
         verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
     }
@@ -1290,16 +1667,15 @@
         // modem retry not invoked.
         AsyncResult res1 = new AsyncResult(0, null,
                 new CommandException(CommandException.Error.INVALID_SIM_STATE));
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res1).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res1).sendToTarget();
         processAllMessages();
         moveTimeForward(5000);
         processAllMessages();
         verify(mMockRadioConfig, times(0)).setPreferredDataModem(eq(0), any());
 
-        doReturn(0).when(mSubscriptionController).getPhoneId(anyInt());
         AsyncResult res2 = new AsyncResult(0, null,
                 new CommandException(CommandException.Error.NETWORK_NOT_READY));
-        Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res2).sendToTarget();
+        Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res2).sendToTarget();
         processAllMessages();
         moveTimeForward(5000);
         processAllMessages();
@@ -1332,6 +1708,45 @@
 
     /* Private utility methods start here */
 
+    private void prepareIdealAutoSwitchCondition() {
+        // 1. service state changes
+        serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        serviceStateChanged(0, NetworkRegistrationInfo
+                .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+        // 2.1 User data enabled on primary SIM
+        doReturn(true).when(mPhone).isUserDataEnabled();
+
+        // 2.2 Auto switch feature is enabled
+        doReturn(true).when(mPhone2).isDataAllowed();
+
+        // 3.1 No default network
+        doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
+
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+    }
+
+    private void serviceStateChanged(int phoneId,
+            @NetworkRegistrationInfo.RegistrationState int dataRegState) {
+
+        ServiceState ss = new ServiceState();
+
+        ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setRegistrationState(dataRegState)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .build());
+
+        ss.setDataRoamingFromRegistration(dataRegState
+                == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+
+        doReturn(ss).when(mPhones[phoneId]).getServiceState();
+
+        Message msg = mPhoneSwitcherUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
+        msg.obj = new AsyncResult(phoneId, null, null);
+        mPhoneSwitcherUT.sendMessage(msg);
+    }
+
     private void setAllPhonesInactive() {
         doReturn(mInactiveCall).when(mPhone).getForegroundCall();
         doReturn(mInactiveCall).when(mPhone).getBackgroundCall();
@@ -1346,43 +1761,46 @@
 
     private void notifyPhoneAsInCall(Phone phone) {
         doReturn(mActiveCall).when(phone).getForegroundCall();
-        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
         processAllMessages();
     }
 
     private void notifyPhoneAsInDial(Phone phone) {
         doReturn(mDialCall).when(phone).getForegroundCall();
-        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
         processAllMessages();
     }
 
     private void notifyPhoneAsInIncomingCall(Phone phone) {
         doReturn(mIncomingCall).when(phone).getForegroundCall();
-        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
         processAllMessages();
     }
 
     private void notifyPhoneAsInHoldingCall(Phone phone) {
         doReturn(mHoldingCall).when(phone).getBackgroundCall();
-        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
         processAllMessages();
     }
 
     private void notifyPhoneAsInactive(Phone phone) {
         doReturn(mInactiveCall).when(phone).getForegroundCall();
-        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
         processAllMessages();
     }
 
     private void notifyDataEnabled(boolean dataEnabled) {
-        doReturn(dataEnabled).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        doReturn(dataEnabled).when(mDataEnabledSettings2).isDataEnabled(anyInt());
-        mPhoneSwitcher.sendEmptyMessage(EVENT_DATA_ENABLED_CHANGED);
+        doReturn(dataEnabled).when(mDataSettingsManager).isDataEnabled();
+        doReturn(dataEnabled).when(mPhone2).isDataAllowed();
+        mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(dataEnabled, 123 , "");
+        if (mDataSettingsManagerCallbacks.size() > 1) {
+            mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(dataEnabled, 123, "");
+        }
         processAllMessages();
     }
 
     private void notifyImsRegistrationTechChange(Phone phone) {
-        mPhoneSwitcher.sendEmptyMessage(EVENT_IMS_RADIO_TECH_CHANGED);
+        mPhoneSwitcherUT.sendEmptyMessage(EVENT_IMS_RADIO_TECH_CHANGED);
         processAllMessages();
     }
 
@@ -1432,10 +1850,10 @@
         for (int i = 0; i < mActiveModemCount; i++) {
             if (defaultDataSub == (i + 1)) {
                 // sub id is always phoneId+1 for testing
-                assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+                assertTrue(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                         new TelephonyNetworkRequest(internetRequest, mPhone), i));
             } else {
-                assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+                assertFalse(mPhoneSwitcherUT.shouldApplyNetworkRequest(
                         new TelephonyNetworkRequest(internetRequest, mPhone), i));
             }
         }
@@ -1455,9 +1873,21 @@
         initializeTelRegistryMock();
         initializeConnManagerMock();
 
-        mPhoneSwitcher = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
+        mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
         processAllMessages();
 
+        Field field = PhoneSwitcher.class.getDeclaredField("mDataSettingsManagerCallbacks");
+        field.setAccessible(true);
+        mDataSettingsManagerCallbacks =
+                (Map<Integer, DataSettingsManager.DataSettingsManagerCallback>)
+                        field.get(mPhoneSwitcherUT);
+
+        int deviceConfigValue = 10000;
+        field = PhoneSwitcher.class.getDeclaredField(
+                "mAutoDataSwitchAvailabilityStabilityTimeThreshold");
+        field.setAccessible(true);
+        field.setInt(mPhoneSwitcherUT, deviceConfigValue);
+
         verify(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
     }
 
@@ -1470,7 +1900,8 @@
         doReturn(0).when(mPhone).getPhoneId();
         doReturn(1).when(mPhone2).getPhoneId();
         doReturn(true).when(mPhone2).isUserDataEnabled();
-        doReturn(mDataEnabledSettings2).when(mPhone2).getDataEnabledSettings();
+        doReturn(mDataSettingsManager2).when(mPhone2).getDataSettingsManager();
+        doReturn(mSST2).when(mPhone2).getServiceStateTracker();
         for (int i = 0; i < supportedModemCount; i++) {
             mSlotIndexToSubId[i] = new int[1];
             mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1548,6 +1979,8 @@
      */
     private void initializeSubControllerMock() {
         doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
+        doReturn(0).when(mSubscriptionController).getPhoneId(1);
+        doReturn(1).when(mSubscriptionController).getPhoneId(2);
         doAnswer(invocation -> {
             int phoneId = (int) invocation.getArguments()[0];
             if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
@@ -1569,6 +2002,8 @@
             }
             return false;
         }).when(mSubscriptionController).isActiveSubId(anyInt());
+        doReturn(new int[mSlotIndexToSubId.length]).when(mSubscriptionController)
+                .getActiveSubIdList(true);
     }
 
     private void setDefaultDataSubId(int defaultDataSub) {
@@ -1579,6 +2014,8 @@
 
     private void setSlotIndexToSubId(int slotId, int subId) {
         mSlotIndexToSubId[slotId][0] = subId;
+        Phone phone = slotId == 0 ? mPhone : mPhone2;
+        doReturn(subId).when(phone).getSubId();
     }
 
     /**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
index 7856564..d528d85 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/QosCallbackTrackerTest.java
@@ -27,15 +27,17 @@
 import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
+import android.net.INetworkAgentRegistry;
 import android.net.InetAddresses;
 import android.net.LinkAddress;
 import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.telephony.data.EpsQos;
 import android.telephony.data.Qos;
 import android.telephony.data.QosBearerFilter;
 import android.telephony.data.QosBearerSession;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -59,6 +61,7 @@
     class Filter implements QosCallbackTracker.IFilter {
         InetSocketAddress mLocalAddress;
         InetSocketAddress mRemoteAddress;
+        int mProtocol = QosBearerFilter.QOS_PROTOCOL_TCP;
 
         Filter(@NonNull final InetSocketAddress localAddress) {
             this.mLocalAddress = localAddress;
@@ -85,11 +88,16 @@
                     && endPort >= mRemoteAddress.getPort()
                     && (address.isAnyLocalAddress() || mRemoteAddress.getAddress().equals(address));
         }
+
+        public boolean matchesProtocol(int protocol) {
+            return mProtocol == protocol;
+        }
     }
 
     // Mocked classes
     private Phone mPhone;
     private TelephonyNetworkAgent mNetworkAgent;
+    private INetworkAgentRegistry mINetworkAgentRegistry;
     private Network mNetwork;
     private RcsStats mRcsStats;
 
@@ -100,6 +108,9 @@
         super.setUp(getClass().getSimpleName());
         mPhone = mock(Phone.class);
         mNetworkAgent = mock(TelephonyNetworkAgent.class);
+        mINetworkAgentRegistry = mock(INetworkAgentRegistry.class);
+        replaceInstance(NetworkAgent.class, "mRegistry", mNetworkAgent, mINetworkAgentRegistry);
+        replaceInstance(NetworkAgent.class, "mPreConnectedQueue", mNetworkAgent, new ArrayList());
         mNetwork = mock(Network.class);
         mRcsStats = mock(RcsStats.class);
         doReturn(mNetwork).when(mNetworkAgent).getNetwork();
@@ -145,6 +156,13 @@
     private static QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
             QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort,
             int precedence) {
+        return createIpv4QosFilter(localAddress, remoteAddress, localPort, remotePort,
+                QosBearerFilter.QOS_PROTOCOL_TCP, precedence);
+    }
+
+    private static QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
+            QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort, int protocol,
+            int precedence) {
         ArrayList<LinkAddress> localAddresses = new ArrayList<>();
         if (localAddress != null) {
             localAddresses.add(
@@ -157,12 +175,11 @@
         }
         return new QosBearerFilter(
                 localAddresses, remoteAddresses, localPort, remotePort,
-                QosBearerFilter.QOS_PROTOCOL_TCP, 7, 987, 678,
+                protocol, 7, 987, 678,
                 QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
     }
 
     @Test
-    @SmallTest
     public void testAddFilterBeforeUpdateSessions() throws Exception {
         Filter filter = new Filter(new InetSocketAddress(
                 InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
@@ -179,8 +196,8 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
-                eq(1234), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         // Matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
@@ -191,14 +208,12 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
 
     @Test
-    @SmallTest
     public void testAddFilterAfterUpdateSessions() throws Exception {
         // Non-matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -222,13 +237,11 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
     @Test
-    @SmallTest
     public void testRemoveFilter() throws Exception {
         // Add filter
         Filter filter = new Filter(new InetSocketAddress(
@@ -246,8 +259,8 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
-                eq(1234), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         // Remove the filter
         mQosCallbackTracker.removeFilter(1);
@@ -262,13 +275,11 @@
         processAllMessages();
 
         // Verify that notifyQosSessionAvailable is not invoked as the filter is already removed
-        verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+        verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
     @Test
-    @SmallTest
     public void testSessionLost() throws Exception {
         // Non-matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -296,19 +307,18 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         // Remove the matching QosBearerFilter
         qosSessions.remove(1);
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1235), eq(1));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
     }
 
     @Test
-    @SmallTest
     public void testModifiedQos() throws Exception {
         // Non-matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -335,10 +345,10 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
-        reset(mNetworkAgent);
+        reset(mINetworkAgentRegistry);
 
         // Update the QOS
         qosSessions.remove(1);
@@ -346,12 +356,11 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
     @Test
-    @SmallTest
     public void testUnmodifiedQos() throws Exception {
         // Non-matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -378,8 +387,8 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         reset(mNetworkAgent);
 
@@ -389,12 +398,11 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
     @Test
-    @SmallTest
     public void testEmptyQosSessions() throws Exception {
         // Add filter
         Filter filter = new Filter(new InetSocketAddress(
@@ -427,21 +435,21 @@
         mQosCallbackTracker.addFilter(2, filter2);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1234), any(EpsBearerQosSessionAttributes.class));
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(2),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
 
         // Update empty QOS sessions list
         mQosCallbackTracker.updateSessions(new ArrayList<>());
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(2), any(QosSession.class));
     }
 
     @Test
-    @SmallTest
     public void testMultipleQosSessions() throws Exception {
         // Add filter 1
         Filter filter1 = new Filter(new InetSocketAddress(
@@ -474,21 +482,20 @@
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1234), any(EpsBearerQosSessionAttributes.class));
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(2),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         // Update empty QOS sessions list
         mQosCallbackTracker.updateSessions(new ArrayList<>());
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(2), any(QosSession.class));
     }
 
     @Test
-    @SmallTest
     public void testQosSessionWithInvalidPortRange() throws Exception {
         // Non-matching QosBearerFilter
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -512,13 +519,11 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
-                eq(1235), any(EpsBearerQosSessionAttributes.class));
-
+        verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
     }
 
     @Test
-    @SmallTest
     public void testQosSessionFilterPortRangeWithoutAddress() throws Exception {
         // QosBearerFilter including remote port range without remote address
         ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
@@ -537,20 +542,87 @@
         mQosCallbackTracker.addFilter(1, filter);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
-                eq(1234), any(EpsBearerQosSessionAttributes.class));
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
 
         // Remove the matching QosBearerFilter
         qosSessions.remove(0);
         mQosCallbackTracker.updateSessions(qosSessions);
         processAllMessages();
 
-        verify(mNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
     }
 
     @Test
-    @SmallTest
-    public void testQosMetrics() throws Exception {
+    public void testQosSessionFilterProtocol() throws Exception {
+        // QosBearerFilter including protocol
+        ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+        qosFilters1.add(createIpv4QosFilter(null, null, null,
+                new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_UDP, 45));
+        ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+        qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+        mQosCallbackTracker.updateSessions(qosSessions);
+
+        // Add filter after updateSessions
+        Filter filter = new Filter(new InetSocketAddress(
+                InetAddresses.parseNumericAddress("122.22.22.22"), 1357),
+                new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 3207));
+        mQosCallbackTracker.addFilter(1, filter);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, never()).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
+        // Remove the matching QosBearerFilter
+        qosSessions.remove(0);
+        mQosCallbackTracker.updateSessions(qosSessions);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, never()).sendQosSessionLost(eq(1), any(QosSession.class));
+
+        qosFilters1.clear();
+        qosFilters1.add(createIpv4QosFilter(null, null, null,
+                new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_TCP, 45));
+        qosSessions.clear();
+        qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+        mQosCallbackTracker.updateSessions(qosSessions);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+        qosSessions.remove(0);
+        mQosCallbackTracker.updateSessions(qosSessions);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+    }
+
+    @Test
+    public void testQosSessionFilterProtocolEsp() throws Exception {
+        // QosBearerFilter including protocol
+        ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+        qosFilters1.add(createIpv4QosFilter(null, null, null,
+                new QosBearerFilter.PortRange(3200, 3220), QosBearerFilter.QOS_PROTOCOL_ESP, 45));
+        ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+        qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+        mQosCallbackTracker.updateSessions(qosSessions);
+
+        // Add filter after updateSessions
+        Filter filter = new Filter(new InetSocketAddress(
+                InetAddresses.parseNumericAddress("122.22.22.22"), 1357),
+                new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 3211));
+        mQosCallbackTracker.addFilter(1, filter);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, times(1)).sendEpsQosSessionAvailable(eq(1),
+                any(QosSession.class), any(EpsBearerQosSessionAttributes.class));
+
+        // Remove the matching QosBearerFilter
+        qosSessions.remove(0);
+        mQosCallbackTracker.updateSessions(qosSessions);
+        processAllMessages();
+        verify(mINetworkAgentRegistry, times(1)).sendQosSessionLost(eq(1), any(QosSession.class));
+    }
+
+    @Test
+    public void testQosMetrics() {
         final int callbackId = 1;
         final int slotId = mPhone.getPhoneId();
         // Add filter before update session
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
index f03194f..7d323aa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
@@ -22,24 +22,18 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
-import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -49,20 +43,14 @@
 
 import com.android.internal.telephony.RadioConfig;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
-import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams.HandoverCallback;
 import com.android.telephony.Rlog;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
@@ -75,13 +63,12 @@
     // Mocked classes
     PhoneSwitcher mPhoneSwitcher;
     private RadioConfig mMockRadioConfig;
-    private DataConnection mDataConnection;
 
     private String mTestName = "";
 
     // List of all requests filed by a test
     private final ArraySet<TelephonyNetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
-    // List of requests active in DcTracker
+    // List of requests active
     private final ArrayList<TelephonyNetworkRequest> mNetworkRequestList = new ArrayList<>();
     // List of complete messages associated with the network requests
     private final Map<TelephonyNetworkRequest, Message> mNetworkRequestMessageMap = new HashMap<>();
@@ -157,7 +144,6 @@
         super.setUp(getClass().getSimpleName());
         mPhoneSwitcher = mock(PhoneSwitcher.class);
         mMockRadioConfig = mock(RadioConfig.class);
-        mDataConnection = mock(DataConnection.class);
         replaceInstance(RadioConfig.class, "sRadioConfig", null, mMockRadioConfig);
 
         mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
@@ -231,7 +217,7 @@
     }
 
     /**
-     * Test that phone active changes cause the DcTracker to get poked.
+     * Test that phone active changes
      */
     @FlakyTest
     @Test
@@ -302,11 +288,10 @@
     }
 
     /**
-     * Test that network request changes cause the DcTracker to get poked.
+     * Test that network request changes
      */
     @Test
     @SmallTest
-    @Ignore("b/256052233")
     public void testRequests() throws Exception {
         mTestName = "testActive";
         final int numberOfPhones = 2;
@@ -366,81 +351,4 @@
         processAllMessages();
         assertEquals(3, mNetworkRequestList.size());
     }
-
-    /**
-     * Test handover when there is no live data connection
-     */
-    @Test
-    @SmallTest
-    @Ignore("b/256052233")
-    public void testHandoverNoLiveData() throws Exception {
-        createMockedTelephonyComponents();
-        doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
-        mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
-                TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
-
-        activatePhoneInPhoneSwitcher(0, true);
-        makeDefaultInternetRequest();
-
-        makeSubSpecificMmsRequest(0);
-        processAllMessages();
-
-        Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
-        f.setAccessible(true);
-        Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);
-
-        HandoverCallback handoverCallback = mock(HandoverCallback.class);
-
-        HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
-                AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
-        AsyncResult ar = new AsyncResult(null, hp, null);
-        h.sendMessage(h.obtainMessage(5, ar));
-        processAllMessages();
-
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
-                .getCurrentTransport(anyInt());
-
-        hp = new HandoverParams(ApnSetting.TYPE_MMS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
-                handoverCallback);
-        ar = new AsyncResult(null, hp, null);
-        h.sendMessage(h.obtainMessage(5, ar));
-        processAllMessages();
-    }
-
-    /**
-     * Test handover when the data connection is being connected.
-     */
-    @Test
-    @SmallTest
-    @Ignore("b/256052233")
-    public void testHandoverActivatingData() throws Exception {
-        createMockedTelephonyComponents();
-        doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
-        mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
-                TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
-
-        activatePhoneInPhoneSwitcher(0, true);
-        makeDefaultInternetRequest();
-
-        makeSubSpecificMmsRequest(0);
-        processAllMessages();
-
-        Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
-        f.setAccessible(true);
-        Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);
-
-        HandoverCallback handoverCallback = mock(HandoverCallback.class);
-        Mockito.reset(mDataNetworkController);
-        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByApnType(anyString());
-        doReturn(false).when(mDataConnection).isActive();
-
-        HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
-                AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
-        AsyncResult ar = new AsyncResult(null, hp, null);
-        h.sendMessage(h.obtainMessage(5, ar));
-        processAllMessages();
-
-        verify(mDataNetworkController, times(1)).removeNetworkRequest(any());
-        verify(mDataNetworkController, times(1)).addNetworkRequest(any());
-    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
deleted file mode 100644
index f2d694f..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ApnConfigTypeRepositoryTest {
-
-    PersistableBundle mCarrierConfig;
-
-    @Before
-    public void setUp() throws Exception {
-        mCarrierConfig = new PersistableBundle();
-    }
-
-    @After
-    public void tearDown() {
-        mCarrierConfig = null;
-    }
-
-    @Test
-    public void testReturnsDefaultsWhenCarrierConfigNull() {
-        ApnConfigTypeRepository repository = new ApnConfigTypeRepository(null);
-        checkDefaults(repository);
-    }
-
-    @Test
-    public void testReturnsDefaultsWhenCarrierConfigApnContextKeyReturnsNull() {
-        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
-                null);
-
-        ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
-        checkDefaults(repository);
-    }
-
-    @Test
-    public void testReturnsDefaultsWhenCarrierConfigHasInvalidTypes() {
-
-        List<String> apnConfigStringArray = new ArrayList<>();
-        apnConfigStringArray.add("xcap,cbs:3");
-        apnConfigStringArray.add("default:0a");
-
-        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
-                apnConfigStringArray.toArray(new String[0]));
-
-        ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
-        checkDefaults(repository);
-    }
-
-    @Test
-    public void testReturnsCarrierConfigOverride() {
-        List<String> apnConfigStringArray = new ArrayList<>();
-        //Shouldn't match or override any keys
-        apnConfigStringArray.add("xcap,cbs:3");
-
-        //Priorities must be integers
-        apnConfigStringArray.add("default:10a");
-
-        //Key isn't case sensitive, which means that this priority should be taken
-        apnConfigStringArray.add("fotA:10");
-
-        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
-                apnConfigStringArray.toArray(new String[0]));
-
-        ApnConfigTypeRepository repository = new ApnConfigTypeRepository(mCarrierConfig);
-        assertEquals(10, repository.getByType(ApnSetting.TYPE_FOTA).getPriority());
-        checkDefaults(repository);
-    }
-
-    private void checkDefaults(ApnConfigTypeRepository repository) {
-        assertEquals(0, repository.getByType(ApnSetting.TYPE_ENTERPRISE).getPriority());
-        assertEquals(1, repository.getByType(ApnSetting.TYPE_DEFAULT).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_MMS).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_SUPL).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_DUN).getPriority());
-        assertEquals(3, repository.getByType(ApnSetting.TYPE_HIPRI).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_IMS).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_CBS).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_IA).getPriority());
-        assertEquals(2, repository.getByType(ApnSetting.TYPE_EMERGENCY).getPriority());
-        assertEquals(3, repository.getByType(ApnSetting.TYPE_MCX).getPriority());
-        assertEquals(3, repository.getByType(ApnSetting.TYPE_XCAP).getPriority());
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
deleted file mode 100644
index b270548..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ApnContextTest extends TelephonyTest {
-    // Mocked classes
-    ApnSetting mApnSetting;
-
-    private ApnContext mApnContext;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        mApnSetting = mock(ApnSetting.class);
-        mApnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mApnContext = null;
-        super.tearDown();
-    }
-
-    @Test
-    @SmallTest
-    public void testSetGetApnSetting() throws Exception {
-        mApnContext.setApnSetting(mApnSetting);
-        assertEquals(mApnSetting, mApnContext.getApnSetting());
-    }
-
-    @Test
-    @SmallTest
-    public void testGetApnType() {
-        assertEquals(ApnSetting.TYPE_DEFAULT_STRING, mApnContext.getApnType());
-    }
-
-    @Test
-    @SmallTest
-    public void testConnectionGeneration() throws Exception {
-        for (int i = 0; i < 100; i++) {
-            mApnContext.incAndGetConnectionGeneration();
-            assertEquals(i + 1, mApnContext.getConnectionGeneration());
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testReason() throws Exception {
-        mApnContext.setReason("dataEnabled");
-        assertEquals("dataEnabled", mApnContext.getReason());
-        mApnContext.setReason("simLoaded");
-        assertEquals("simLoaded", mApnContext.getReason());
-    }
-
-    @Test
-    @SmallTest
-    public void testState() throws Exception {
-        mApnContext.setState(DctConstants.State.DISCONNECTING);
-        assertEquals(DctConstants.State.DISCONNECTING, mApnContext.getState());
-        mApnContext.setEnabled(true);
-        assertFalse(mApnContext.isConnectable());
-
-        mApnContext.setState(DctConstants.State.RETRYING);
-        assertTrue(mApnContext.isConnectable());
-        assertTrue(mApnContext.isConnectedOrConnecting());
-
-        mApnContext.setState(DctConstants.State.FAILED);
-        assertTrue(mApnContext.isDisconnected());
-        mApnContext.setState(DctConstants.State.IDLE);
-        assertTrue(mApnContext.isDisconnected());
-    }
-
-    @Test
-    @SmallTest
-    public void testNetworkRequestNormal() throws Exception {
-        NetworkRequest nr1 = new NetworkRequest.Builder().build();
-        mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
-        NetworkRequest nr2 = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .build();
-
-        mApnContext.requestNetwork(nr2, DcTracker.REQUEST_TYPE_NORMAL, null);
-        verify(mDcTracker, times(2)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
-        mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_NORMAL);
-        verify(mDcTracker, never()).disableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.RELEASE_TYPE_NORMAL));
-
-        mApnContext.releaseNetwork(nr2, DcTracker.RELEASE_TYPE_NORMAL);
-        verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.RELEASE_TYPE_NORMAL));
-    }
-
-    @Test
-    @SmallTest
-    public void testNetworkRequestDetach() throws Exception {
-        NetworkRequest nr1 = new NetworkRequest.Builder().build();
-        mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_NORMAL, null);
-        verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
-        NetworkRequest nr2 = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .build();
-
-        mApnContext.requestNetwork(nr2, DcTracker.REQUEST_TYPE_NORMAL, null);
-        verify(mDcTracker, times(2)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.REQUEST_TYPE_NORMAL), eq(null));
-
-        mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_DETACH);
-        verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.RELEASE_TYPE_DETACH));
-
-        mApnContext.releaseNetwork(nr2, DcTracker.RELEASE_TYPE_NORMAL);
-        verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.RELEASE_TYPE_NORMAL));
-    }
-
-    @Test
-    @SmallTest
-    public void testNetworkRequestHandover() throws Exception {
-        NetworkRequest nr1 = new NetworkRequest.Builder().build();
-        mApnContext.requestNetwork(nr1, DcTracker.REQUEST_TYPE_HANDOVER, null);
-        verify(mDcTracker, times(1)).enableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.REQUEST_TYPE_HANDOVER), eq(null));
-
-        mApnContext.releaseNetwork(nr1, DcTracker.RELEASE_TYPE_HANDOVER);
-        verify(mDcTracker, times(1)).disableApn(eq(ApnSetting.TYPE_DEFAULT),
-                eq(DcTracker.RELEASE_TYPE_HANDOVER));
-    }
-
-    @Test
-    @SmallTest
-    public void testConcurrentVoiceAndDataAllowed() throws Exception {
-        mApnContext.setConcurrentVoiceAndDataAllowed(true);
-        assertTrue(mApnContext.isConcurrentVoiceAndDataAllowed());
-        mApnContext.setConcurrentVoiceAndDataAllowed(false);
-        assertFalse(mApnContext.isConcurrentVoiceAndDataAllowed());
-    }
-
-    @Test
-    @SmallTest
-    public void testEnableDisable() throws Exception {
-        mApnContext.setEnabled(true);
-        assertTrue(mApnContext.isEnabled());
-        mApnContext.setEnabled(false);
-        assertFalse(mApnContext.isEnabled());
-    }
-
-    @Test
-    @SmallTest
-    public void testProvisionApn() throws Exception {
-        mContextFixture.putResource(R.string.mobile_provisioning_apn, "fake_apn");
-
-        ApnSetting myApn = new ApnSetting.Builder()
-                .setId(2163)
-                .setOperatorNumeric("44010")
-                .setEntryName("sp-mode")
-                .setApnName("fake_apn")
-                .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
-                .setProtocol(ApnSetting.PROTOCOL_IP)
-                .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-                .setCarrierEnabled(true)
-                .build();
-
-        mApnContext.setApnSetting(myApn);
-        assertTrue(mApnContext.isProvisioningApn());
-        mApnContext.setApnSetting(mApnSetting);
-        assertFalse(mApnContext.isProvisioningApn());
-    }
-
-    @Test
-    @SmallTest
-    public void testIsReady() throws Exception {
-        mApnContext.setEnabled(true);
-        assertTrue(mApnContext.isReady());
-
-        mApnContext.setEnabled(false);
-        assertFalse(mApnContext.isReady());
-    }
-
-    @Test
-    @SmallTest
-    public void testErrorCodeRetry() throws Exception {
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_cell_retries_per_error_code,
-                new String[]{"36,2"});
-        mApnContext.resetErrorCodeRetries();
-
-        assertFalse(mApnContext.restartOnError(36));
-        assertTrue(mApnContext.restartOnError(36));
-        assertFalse(mApnContext.restartOnError(37));
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
deleted file mode 100644
index dcaa2a0..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ /dev/null
@@ -1,1479 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.net.InetAddresses;
-import android.net.KeepalivePacketData;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketData;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.vcn.VcnNetworkPolicyResult;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.UserManager;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
-import android.telephony.ServiceState.RegState;
-import android.telephony.ServiceState.RilRadioTechnology;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.DataServiceCallback;
-import android.telephony.data.TrafficDescriptor;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
-
-import com.android.internal.R;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.data.KeepaliveStatus;
-import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
-import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
-import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
-import com.android.internal.telephony.metrics.DataCallSessionStats;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.function.Consumer;
-
-public class DataConnectionTest extends TelephonyTest {
-    private static final int DEFAULT_DC_CID = 10;
-    private static final ArrayList<TrafficDescriptor> DEFAULT_TD_LIST = new ArrayList<>();
-
-    // Mocked classes
-    DcTesterFailBringUpAll mDcTesterFailBringUpAll;
-    ConnectionParams mCp;
-    DisconnectParams mDcp;
-    ApnContext mApnContext;
-    ApnContext mEnterpriseApnContext;
-    DcFailBringUp mDcFailBringUp;
-    DataCallSessionStats mDataCallSessionStats;
-    DataConnection mDefaultDc;
-    DataServiceManager mDataServiceManager;
-
-    private DataConnection mDc;
-    private DataConnectionTestHandler mDataConnectionTestHandler;
-    private DcController mDcc;
-
-    private final ApnSetting mApn1 = new ApnSetting.Builder()
-            .setId(2163)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private final ApnSetting mApn2 = new ApnSetting.Builder()
-            .setId(2164)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private final ApnSetting mApn3 = new ApnSetting.Builder()
-            .setId(2165)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
-            .setProtocol(ApnSetting.PROTOCOL_IPV6)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setNetworkTypeBitmask(0)
-            .setCarrierEnabled(true)
-            .setCarrierId(1)
-            .setSkip464Xlat(1)
-            .build();
-
-    private final ApnSetting mApn4 = new ApnSetting.Builder()
-            .setId(2166)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_IMS)
-            .setProtocol(ApnSetting.PROTOCOL_IPV6)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private final ApnSetting mApn5 = new ApnSetting.Builder()
-            .setId(2167)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_IMS)
-            .setProtocol(ApnSetting.PROTOCOL_IPV6)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .setSkip464Xlat(Telephony.Carriers.SKIP_464XLAT_DISABLE)
-            .build();
-
-    private final ApnSetting mApn6 = new ApnSetting.Builder()
-            .setId(2168)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private class DataConnectionTestHandler extends HandlerThread {
-
-        private DataConnectionTestHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            Handler h = new Handler();
-            mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, h.getLooper(), "");
-            mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
-                    mDcTesterFailBringUpAll, mDcc);
-        }
-    }
-
-    private void setSuccessfulSetupDataResponse(int cid, ArrayList<TrafficDescriptor> tds) {
-        doAnswer(invocation -> {
-            final Message msg = (Message) invocation.getArguments()[10];
-
-            DataCallResponse response = new DataCallResponse.Builder()
-                    .setCause(0)
-                    .setRetryDurationMillis(-1L)
-                    .setId(cid)
-                    .setLinkStatus(2)
-                    .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
-                    .setInterfaceName("ifname")
-                    .setAddresses(Arrays.asList(
-                            new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
-                            new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
-                    .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
-                            InetAddresses.parseNumericAddress("fd00:976a::9")))
-                    .setGatewayAddresses(Arrays.asList(
-                            InetAddresses.parseNumericAddress("10.0.2.15"),
-                            InetAddresses.parseNumericAddress("fe80::2")))
-                    .setPcscfAddresses(Arrays.asList(
-                            InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
-                            InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
-                            InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
-                    .setMtu(1500)
-                    .setMtuV4(1500)
-                    .setMtuV6(1500)
-                    .setPduSessionId(1)
-                    .setQosBearerSessions(new ArrayList<>())
-                    .setTrafficDescriptors(tds)
-                    .build();
-            msg.getData().putParcelable("data_call_response", response);
-            msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
-            msg.sendToTarget();
-            return null;
-        }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
-                anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
-                any(Message.class));
-    }
-
-    private void setFailedSetupDataResponse(@DataServiceCallback.ResultCode int resultCode) {
-        doAnswer(invocation -> {
-            final Message msg = (Message) invocation.getArguments()[10];
-            msg.arg1 = resultCode;
-            msg.sendToTarget();
-            return null;
-        }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
-                anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
-                any(Message.class));
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        mDcTesterFailBringUpAll = mock(DcTesterFailBringUpAll.class);
-        mCp = mock(ConnectionParams.class);
-        mDcp = mock(DisconnectParams.class);
-        mApnContext = mock(ApnContext.class);
-        mEnterpriseApnContext = mock(ApnContext.class);
-        mDcFailBringUp = mock(DcFailBringUp.class);
-        mDataCallSessionStats = mock(DataCallSessionStats.class);
-        mDefaultDc = mock(DataConnection.class);
-        mDataServiceManager = mock(DataServiceManager.class);
-        logd("+Setup!");
-        doReturn("fake.action_detached").when(mPhone).getActionDetached();
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
-        replaceInstance(ConnectionParams.class, "mRilRat", mCp,
-                ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
-        doReturn(mApn1).when(mApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_DEFAULT_STRING).when(mApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
-
-        mDcFailBringUp.saveParameters(0, 0, -2);
-        doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
-
-        mContextFixture.putStringArrayResource(com.android.internal.R.array
-                .config_mobile_tcp_buffers, new String[]{
-                "umts:131072,262144,1452032,4096,16384,399360",
-                "hspa:131072,262144,2441216,4096,16384,399360",
-                "hsupa:131072,262144,2441216,4096,16384,399360",
-                "hsdpa:131072,262144,2441216,4096,16384,399360",
-                "hspap:131072,262144,2441216,4096,16384,399360",
-                "edge:16384,32768,131072,4096,16384,65536",
-                "gprs:4096,8192,24576,4096,8192,24576",
-                "1xrtt:16384,32768,131070,4096,16384,102400",
-                "evdo:131072,262144,1048576,4096,16384,524288",
-                "lte:524288,1048576,8388608,262144,524288,4194304"});
-
-        mContextFixture.putResource(R.string.config_wwan_data_service_package,
-                "com.android.phone");
-
-        mDcp.mApnContext = mApnContext;
-
-        setSuccessfulSetupDataResponse(DEFAULT_DC_CID, DEFAULT_TD_LIST);
-
-        doAnswer(invocation -> {
-            final Message msg = (Message) invocation.getArguments()[2];
-            msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
-            msg.sendToTarget();
-            return null;
-        }).when(mDataServiceManager).deactivateDataCall(anyInt(), anyInt(), any(Message.class));
-
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mDataServiceManager)
-                .getTransportType();
-
-        mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
-        mDataConnectionTestHandler.start();
-
-        waitForMs(200);
-        mDc.setDataCallSessionStats(mDataCallSessionStats);
-
-        logd("-Setup!");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        logd("tearDown");
-        mDc.quitNow();
-        mDc = null;
-        mDataConnectionTestHandler.quit();
-        mDataConnectionTestHandler.join();
-        mDataConnectionTestHandler = null;
-        mDcc.removeCallbacksAndMessages(null);
-        mDcc = null;
-        DEFAULT_TD_LIST.clear();
-        waitForMs(100);
-        super.tearDown();
-    }
-
-    private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
-        Class[] cArgs = new Class[1];
-        cArgs[0] = DataCallResponse.class;
-        Method method = DataConnection.class.getDeclaredMethod("getSuggestedRetryDelay", cArgs);
-        method.setAccessible(true);
-        return (long) method.invoke(mDc, response);
-    }
-
-    private boolean isUnmeteredUseOnly() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("isUnmeteredUseOnly");
-        method.setAccessible(true);
-        return (boolean) method.invoke(mDc);
-    }
-
-    private boolean isEnterpriseUse() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("isEnterpriseUse");
-        method.setAccessible(true);
-        return (boolean) method.invoke(mDc);
-    }
-
-    private boolean isSuspended() throws Exception {
-        Field field = DataConnection.class.getDeclaredField("mIsSuspended");
-        field.setAccessible(true);
-        return field.getBoolean(mDc);
-    }
-
-    private SetupResult setLinkProperties(DataCallResponse response, LinkProperties linkProperties)
-            throws Exception {
-        Class[] cArgs = new Class[2];
-        cArgs[0] = DataCallResponse.class;
-        cArgs[1] = LinkProperties.class;
-        Method method = DataConnection.class.getDeclaredMethod("setLinkProperties", cArgs);
-        method.setAccessible(true);
-        return (SetupResult) method.invoke(mDc, response, linkProperties);
-    }
-
-    @Test
-    @SmallTest
-    public void testConnectEvent() {
-        assertTrue(mDc.isInactive());
-        connectEvent(true);
-
-        verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
-                eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
-        verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
-                eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
-        verify(mSimulatedCommandsVerifier, times(1))
-                .registerForNattKeepaliveStatus(any(Handler.class),
-                        eq(DataConnection.EVENT_KEEPALIVE_STATUS), eq(null));
-        verify(mSimulatedCommandsVerifier, times(1))
-                .registerForLceInfo(any(Handler.class),
-                        eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
-        verify(mVcnManager, atLeastOnce())
-                .applyVcnNetworkPolicy(
-                        argThat(caps ->
-                                caps.hasCapability(
-                                        NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
-                        any());
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mDataServiceManager, times(1)).setupDataCall(
-                eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
-                eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
-
-        verify(mSimulatedCommandsVerifier, times(0))
-                .allocatePduSessionId(any());
-
-        assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
-        if (tdCaptor.getValue() != null) {
-            if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-                assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-                assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                        tdCaptor.getValue().getOsAppId()));
-            } else {
-                assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
-                assertEquals(null, tdCaptor.getValue().getOsAppId());
-            }
-        }
-        assertTrue(mDc.isActive());
-
-        assertEquals(1, mDc.getPduSessionId());
-        assertEquals(3, mDc.getPcscfAddresses().length);
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::5"::equals));
-    }
-
-    @Test
-    @SmallTest
-    public void testConnectOnIwlan() throws Exception {
-        assertTrue(mDc.isInactive());
-        Field field = DataConnection.class.getDeclaredField("mTransportType");
-        field.setAccessible(true);
-        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-        connectEvent(true);
-
-        verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
-                eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
-        verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
-                eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
-        verify(mSimulatedCommandsVerifier, times(0))
-                .registerForNattKeepaliveStatus(any(Handler.class),
-                        eq(DataConnection.EVENT_KEEPALIVE_STATUS), eq(null));
-        verify(mSimulatedCommandsVerifier, times(0))
-                .registerForLceInfo(any(Handler.class),
-                        eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
-        verify(mVcnManager, atLeastOnce())
-                .applyVcnNetworkPolicy(
-                        argThat(caps ->
-                                caps.hasCapability(
-                                        NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
-                        any());
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mDataServiceManager, times(1)).setupDataCall(
-                eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
-                eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
-
-        verify(mSimulatedCommandsVerifier, times(1))
-                .allocatePduSessionId(any());
-
-        assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
-        if (tdCaptor.getValue() != null) {
-            if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
-                assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-                assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                        tdCaptor.getValue().getOsAppId()));
-            } else {
-                assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
-                assertEquals(null, tdCaptor.getValue().getOsAppId());
-            }
-        }
-        assertTrue(mDc.isActive());
-
-        assertEquals(1, mDc.getPduSessionId());
-        assertEquals(3, mDc.getPcscfAddresses().length);
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
-        assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::5"::equals));
-    }
-
-    @Test
-    public void testConnectEventDuplicateContextIds() throws Exception {
-        setUpDefaultData(DEFAULT_DC_CID);
-
-        // Try to connect ENTERPRISE with the same CID as default
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-
-        // Verify that ENTERPRISE wasn't set up
-        connectEvent(false);
-        assertTrue(mDc.isInactive());
-
-        // Change the CID
-        setSuccessfulSetupDataResponse(DEFAULT_DC_CID + 1, DEFAULT_TD_LIST);
-
-        // Verify that ENTERPRISE was set up
-        connectEvent(true);
-        assertTrue(mDc.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-    }
-
-    @Test
-    public void testConnectEventDuplicateContextIdsDifferentTDs() throws Exception {
-        setUpDefaultData(DEFAULT_DC_CID);
-
-        // Try to connect ENTERPRISE with the same CID as default but different TrafficDescriptors
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-        ArrayList<TrafficDescriptor> tdList = new ArrayList<>();
-        tdList.add(new TrafficDescriptor("dnn", DataConnection.getEnterpriseOsAppId()));
-        setSuccessfulSetupDataResponse(DEFAULT_DC_CID, tdList);
-
-        // Verify that ENTERPRISE wasn't set up but the TD list was updated
-        connectEvent(false);
-        assertTrue(mDc.isInactive());
-        ArgumentCaptor<DataCallResponse> captor = ArgumentCaptor.forClass(DataCallResponse.class);
-        verify(mDefaultDc).updateTrafficDescriptors(captor.capture());
-        assertEquals(tdList, captor.getValue().getTrafficDescriptors());
-    }
-
-    @Test
-    public void testConnectEventNoDefaultData() throws Exception {
-        assertFalse(mDefaultDc.isActive());
-
-        // Try to connect ENTERPRISE when default data doesn't exist
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-
-        // Verify that ENTERPRISE wasn't set up
-        connectEvent(false);
-        assertTrue(mDc.isInactive());
-
-        // Set up default data
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
-        setUpDefaultData(1);
-
-        // Verify that ENTERPRISE was set up
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        connectEvent(true);
-        assertTrue(mDc.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-    }
-
-    private void setUpDefaultData(int cid) throws Exception {
-        replaceInstance(DataConnection.class, "mCid", mDefaultDc, cid);
-        doReturn(true).when(mDefaultDc).isActive();
-        doReturn(Arrays.asList(mApnContext)).when(mDefaultDc).getApnContexts();
-        mDcc.addActiveDcByCid(mDefaultDc);
-        assertTrue(mDefaultDc.getApnContexts().stream()
-                .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
-    }
-
-    @Test
-    @SmallTest
-    public void testDisconnectEvent() {
-        testConnectEvent();
-
-        mDc.setPduSessionId(5);
-        disconnectEvent();
-
-        verify(mSimulatedCommandsVerifier, times(1)).unregisterForLceInfo(any(Handler.class));
-        verify(mSimulatedCommandsVerifier, times(1))
-                .unregisterForNattKeepaliveStatus(any(Handler.class));
-        verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
-                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
-        verify(mSimulatedCommandsVerifier, times(0))
-                .releasePduSessionId(any(), eq(5));
-
-        assertTrue(mDc.isInactive());
-    }
-
-    @Test
-    @SmallTest
-    public void testDisconnectOnIwlan() throws Exception {
-        testConnectEvent();
-
-        Field field = DataConnection.class.getDeclaredField("mTransportType");
-        field.setAccessible(true);
-        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-        mDc.setPduSessionId(5);
-        disconnectEvent();
-
-        verify(mSimulatedCommandsVerifier, times(0)).unregisterForLceInfo(any(Handler.class));
-        verify(mSimulatedCommandsVerifier, times(0))
-                .unregisterForNattKeepaliveStatus(any(Handler.class));
-        verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
-                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1))
-                .releasePduSessionId(any(), eq(5));
-
-        assertTrue(mDc.isInactive());
-    }
-
-    @Test
-    @SmallTest
-    public void testModemSuggestRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(0)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
-
-        response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(1000)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
-
-        response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(9999)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(response.getSuggestedRetryTime(), getSuggestedRetryDelay(response));
-    }
-
-    @Test
-    @SmallTest
-    public void testModemNotSuggestRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
-
-        response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-5)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
-
-        response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(Long.MIN_VALUE)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(response));
-    }
-
-    @Test
-    @SmallTest
-    public void testModemSuggestNoRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(Long.MAX_VALUE)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
-    }
-
-    private NetworkCapabilities getNetworkCapabilities() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
-        method.setAccessible(true);
-        return (NetworkCapabilities) method.invoke(mDc);
-    }
-
-    private int getDisallowedApnTypes() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("getDisallowedApnTypes");
-        method.setAccessible(true);
-        return (int) method.invoke(mDc);
-    }
-
-    @Test
-    @SmallTest
-    public void testNetworkCapability() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        doReturn(mApn2).when(mApnContext).getApnSetting();
-        testConnectEvent();
-
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
-                new String[] {"supl"});
-
-        disconnectEvent();
-        doReturn(mApn1).when(mApnContext).getApnSetting();
-        connectEvent(true);
-
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-    }
-
-    @Test
-    @SmallTest
-    public void testVcnNetworkCapability() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        doReturn(mApn2).when(mApnContext).getApnSetting();
-
-        doAnswer(invocation -> {
-            NetworkCapabilities nc = invocation.getArgument(0);
-            NetworkCapabilities policyNc = new NetworkCapabilities.Builder(nc)
-                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                    .build();
-
-            return new VcnNetworkPolicyResult(
-                    false /* isTearDownRequested */, policyNc);
-        }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
-        connectEvent(true);
-
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
-
-        disconnectEvent();
-
-        doAnswer(invocation -> {
-            NetworkCapabilities nc = invocation.getArgument(0);
-            NetworkCapabilities policyNc = new NetworkCapabilities.Builder(nc)
-                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                    .build();
-
-            return new VcnNetworkPolicyResult(
-                    false /* isTearDownRequested */, policyNc);
-        }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
-        connectEvent(true);
-
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
-    }
-
-    @Test
-    @SmallTest
-    public void testEnterpriseNetworkCapability() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        doReturn(mApn2).when(mApnContext).getApnSetting();
-        testConnectEvent();
-
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
-        disconnectEvent();
-        setUpDefaultData(1);
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-        connectEvent(true);
-
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-    }
-
-    @Test
-    @SmallTest
-    public void testMeteredCapability() throws Exception {
-
-        mContextFixture.getCarrierConfigBundle().
-                putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] {"default"});
-
-        testConnectEvent();
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-    }
-
-    @Test
-    @SmallTest
-    public void testNonMeteredCapability() throws Exception {
-
-        doReturn(2819).when(mPhone).getSubId();
-        mContextFixture.getCarrierConfigBundle().
-                putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                        new String[] {"mms"});
-
-        testConnectEvent();
-
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-    }
-
-    @Test
-    public void testOverrideUnmetered() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        testConnectEvent();
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
-        mDc.onMeterednessChanged(true);
-        waitForMs(100);
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
-        mDc.onMeterednessChanged(false);
-        waitForMs(100);
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-    }
-
-    @Test
-    public void testOverrideCongested() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        testConnectEvent();
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
-        mDc.onCongestednessChanged(true);
-        waitForMs(100);
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-
-        mDc.onCongestednessChanged(false);
-        waitForMs(100);
-
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
-        assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-        assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
-    }
-
-    @Test
-    public void testOwnerUid() throws Exception {
-        Context mockContext = mContextFixture.getTestDouble();
-        doReturn(mockContext).when(mPhone).getContext();
-
-        String testPkg = "com.android.telephony.test";
-        TelephonyManager telMgr = mockContext.getSystemService(TelephonyManager.class);
-        doReturn(testPkg).when(telMgr).getCarrierServicePackageNameForLogicalSlot(anyInt());
-
-        UserInfo info = new UserInfo(0 /* id */, "TEST_USER", 0 /* flags */);
-        UserManager userMgr = mockContext.getSystemService(UserManager.class);
-        doReturn(Collections.singletonList(info)).when(userMgr).getUsers();
-
-        int carrierConfigPkgUid = 12345;
-        PackageManager pkgMgr = mockContext.getPackageManager();
-        doReturn(carrierConfigPkgUid).when(pkgMgr).getPackageUidAsUser(eq(testPkg), anyInt());
-
-        mContextFixture
-                .getCarrierConfigBundle()
-                .putStringArray(
-                        CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                        new String[] {"default"});
-        testConnectEvent();
-        AsyncResult adminUidsResult = new AsyncResult(null, new int[] {carrierConfigPkgUid}, null);
-        mDc.sendMessage(DataConnection.EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, adminUidsResult);
-        // Wait for carirer privilege UIDs to be updated
-        waitForMs(100);
-
-        assertEquals(carrierConfigPkgUid, getNetworkCapabilities().getOwnerUid());
-        assertEquals(
-                Collections.singleton(carrierConfigPkgUid),
-                getNetworkCapabilities().getAllowedUids());
-    }
-
-    @Test
-    public void testSubscriptionIds() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-        testConnectEvent();
-
-        assertEquals(Collections.singleton(0), getNetworkCapabilities().getSubscriptionIds());
-    }
-
-    @Test
-    public void testShouldSkip464Xlat() throws Exception {
-        assertFalse(testShouldSkip464XlatEvent(mApn1));
-        disconnectEvent();
-
-        assertTrue(testShouldSkip464XlatEvent(mApn3));
-        disconnectEvent();
-
-        assertTrue(testShouldSkip464XlatEvent(mApn4));
-        disconnectEvent();
-
-        assertFalse(testShouldSkip464XlatEvent(mApn5));
-        disconnectEvent();
-    }
-
-    private boolean testShouldSkip464XlatEvent(ApnSetting apn) throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("shouldSkip464Xlat");
-        method.setAccessible(true);
-
-        doReturn(apn).when(mApnContext).getApnSetting();
-        doReturn(apn.getApnTypeBitmask()).when(mApnContext).getApnTypeBitmask();
-        connectEvent(true);
-        logd(getNetworkCapabilities().toString());
-
-        return (Boolean) method.invoke(mDc);
-    }
-
-    private void connectEvent(boolean validate) {
-        mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
-        waitForMs(200);
-        if (validate) {
-            assertTrue(mDc.isActive());
-        }
-    }
-
-    private void disconnectEvent() {
-        mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
-        waitForMs(100);
-        assertTrue(mDc.isInactive());
-    }
-
-    private void serviceStateChangedEvent(@RegState int dataRegState, @RilRadioTechnology int rat) {
-        mDc.obtainMessage(DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED,
-                new AsyncResult(null, new Pair<>(dataRegState, rat), null)).sendToTarget();
-        waitForMs(100);
-    }
-
-    @Test
-    @SmallTest
-    public void testIsIpAddress() {
-        // IPv4
-        assertTrue(DataConnection.isIpAddress("1.2.3.4"));
-        assertTrue(DataConnection.isIpAddress("127.0.0.1"));
-
-        // IPv6
-        assertTrue(DataConnection.isIpAddress("::1"));
-        assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
-    }
-
-    @Test
-    @SmallTest
-    public void testSetLinkProperties() throws Exception {
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-
-        LinkProperties linkProperties = new LinkProperties();
-        assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
-        logd(linkProperties.toString());
-        assertEquals(response.getInterfaceName(), linkProperties.getInterfaceName());
-        assertEquals(response.getAddresses().size(), linkProperties.getAddresses().size());
-        for (int i = 0; i < response.getAddresses().size(); ++i) {
-            assertEquals(response.getAddresses().get(i).getAddress(),
-                    InetAddresses.parseNumericAddress(linkProperties.getLinkAddresses().get(i)
-                            .getAddress().getHostAddress()));
-        }
-
-        assertEquals(response.getDnsAddresses().size(), linkProperties.getDnsServers().size());
-        for (int i = 0; i < response.getDnsAddresses().size(); ++i) {
-            assertEquals("i = " + i, response.getDnsAddresses().get(i),
-                    InetAddresses.parseNumericAddress(
-                            linkProperties.getDnsServers().get(i).getHostAddress()));
-        }
-
-        assertEquals(response.getGatewayAddresses().size(), linkProperties.getRoutes().size());
-        for (int i = 0; i < response.getGatewayAddresses().size(); ++i) {
-            assertEquals("i = " + i, response.getGatewayAddresses().get(i),
-                    InetAddresses.parseNumericAddress(linkProperties.getRoutes().get(i)
-                            .getGateway().getHostAddress()));
-        }
-
-        assertEquals(response.getPcscfAddresses().size(), linkProperties.getPcscfServers().size());
-        for (int i = 0; i < response.getPcscfAddresses().size(); ++i) {
-            assertEquals("i = " + i, response.getPcscfAddresses().get(i),
-                    InetAddresses.parseNumericAddress(linkProperties.getPcscfServers().get(i)
-                            .getHostAddress()));
-        }
-
-        assertEquals(response.getMtu(), linkProperties.getMtu());
-    }
-
-    @Test
-    @SmallTest
-    public void testSetLinkPropertiesEmptyAddress() throws Exception {
-        // 224.224.224.224 is an invalid address.
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-
-        LinkProperties linkProperties = new LinkProperties();
-        assertEquals(SetupResult.ERROR_INVALID_ARG, setLinkProperties(response, linkProperties));
-    }
-
-    @Test
-    @SmallTest
-    public void testSetLinkPropertiesEmptyDns() throws Exception {
-        // Empty dns entry.
-        DataCallResponse response = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(2)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-
-        // Make sure no exception was thrown
-        LinkProperties linkProperties = new LinkProperties();
-        assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
-    }
-
-    @Test
-    @SmallTest
-    public void testStartKeepaliveWLAN() throws Exception {
-        testConnectEvent();
-        waitForMs(200);
-
-        Field field = DataConnection.class.getDeclaredField("mTransportType");
-        field.setAccessible(true);
-        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-
-        final int sessionHandle = 0xF00;
-        final int slotId = 3;
-        final int interval = 10; // seconds
-        // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
-        // and check that the packet is sent to the RIL.
-        KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
-                InetAddresses.parseNumericAddress("1.2.3.4"),
-                1234,
-                InetAddresses.parseNumericAddress("8.8.8.8"),
-                4500);
-        mDc.obtainMessage(
-                DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
-        waitForMs(100);
-        // testStartStopNattKeepalive() verifies that this request is passed with WWAN.
-        // Thus, even though we can't see the response in NetworkAgent, we can verify that the
-        // CommandsInterface never receives a request and infer that it was dropped due to WLAN.
-        verify(mSimulatedCommandsVerifier, times(0))
-                .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
-    }
-
-    public void checkStartStopNattKeepalive(boolean useCondensedFlow) throws Exception {
-        testConnectEvent();
-        waitForMs(200);
-
-        final int sessionHandle = 0xF00;
-        final int slotId = 3;
-        final int interval = 10; // seconds
-        // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
-        // and check that the packet is sent to the RIL.
-        KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
-                InetAddresses.parseNumericAddress("1.2.3.4"),
-                1234,
-                InetAddresses.parseNumericAddress("8.8.8.8"),
-                4500);
-        mDc.obtainMessage(
-                DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
-        waitForMs(100);
-        verify(mSimulatedCommandsVerifier, times(1))
-                .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
-
-        Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
-        if (useCondensedFlow) {
-            // Send a singled condensed response that a keepalive have been requested and the
-            // activation is completed. This flow should be used if the keepalive offload request
-            // is handled by a high-priority signalling path.
-            AsyncResult.forMessage(
-                    kaStarted, new KeepaliveStatus(
-                            sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
-            kaStarted.sendToTarget();
-        } else {
-            // Send the sequential responses indicating first that the request was received and
-            // then that the keepalive is running. This should create an active record of the
-            // keepalive in DataConnection while permitting the status from a low priority or other
-            // high-latency handler to activate the keepalive without blocking a request.
-            AsyncResult.forMessage(
-                    kaStarted, new KeepaliveStatus(
-                            sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
-            kaStarted.sendToTarget();
-            Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
-            AsyncResult.forMessage(
-                    kaRunning, new KeepaliveStatus(
-                            sessionHandle, KeepaliveStatus.STATUS_ACTIVE), null);
-            kaRunning.sendToTarget();
-        }
-        waitForMs(100);
-
-        // Verify that we can stop the connection, which checks that the record in DataConnection
-        // has a valid mapping between slotId (from network agent) to sessionHandle (from Radio).
-        mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
-        waitForMs(100);
-        verify(mSimulatedCommandsVerifier, times(1))
-                .stopNattKeepalive(eq(sessionHandle), any(Message.class));
-
-        Message kaStopped = mDc.obtainMessage(
-                DataConnection.EVENT_KEEPALIVE_STOPPED, sessionHandle, slotId);
-        AsyncResult.forMessage(kaStopped);
-        kaStopped.sendToTarget();
-        // Verify that after the connection is stopped, the mapping for a Keepalive Session is
-        // removed. Thus, subsequent calls to stop the same keepalive are ignored.
-        mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
-        waitForMs(100);
-        // Check that the mock has not been called subsequent to the previous invocation
-        // while avoiding the use of reset()
-        verify(mSimulatedCommandsVerifier, times(1))
-                .stopNattKeepalive(anyInt(), any(Message.class));
-    }
-
-    @Test
-    @MediumTest
-    public void testStartStopNattKeepalive() throws Exception {
-        checkStartStopNattKeepalive(false);
-    }
-
-    @Test
-    @MediumTest
-    public void testStartStopNattKeepaliveCondensed() throws Exception {
-        checkStartStopNattKeepalive(true);
-    }
-
-    public void checkStartNattKeepaliveFail(boolean useCondensedFlow) throws Exception {
-        testConnectEvent();
-        waitForMs(200);
-
-        final int sessionHandle = 0xF00;
-        final int slotId = 3;
-        final int interval = 10; // seconds
-        // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
-        // and check that the packet is sent to the RIL.
-        KeepalivePacketData kd = NattKeepalivePacketData.nattKeepalivePacket(
-                InetAddresses.parseNumericAddress("1.2.3.4"),
-                1234,
-                InetAddresses.parseNumericAddress("8.8.8.8"),
-                4500);
-        mDc.obtainMessage(
-                DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
-        waitForMs(100);
-        verify(mSimulatedCommandsVerifier, times(1))
-                .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
-
-        Message kaStarted = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STARTED, slotId, 0);
-        if (useCondensedFlow) {
-            // Indicate in the response that the keepalive has failed.
-            AsyncResult.forMessage(
-                    kaStarted, new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED),
-                    null);
-            kaStarted.sendToTarget();
-        } else {
-            // Indicate that the keepalive is queued, and then signal a failure from the modem
-            // such that a pending keepalive fails to activate.
-            AsyncResult.forMessage(
-                    kaStarted, new KeepaliveStatus(
-                            sessionHandle, KeepaliveStatus.STATUS_PENDING), null);
-            kaStarted.sendToTarget();
-            Message kaRunning = mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STATUS);
-            AsyncResult.forMessage(
-                    kaRunning, new KeepaliveStatus(
-                            sessionHandle, KeepaliveStatus.STATUS_INACTIVE), null);
-            kaRunning.sendToTarget();
-        }
-        waitForMs(100);
-        // Verify that a failed connection request cannot be stopped due to no record in
-        // the DataConnection.
-        mDc.obtainMessage(DataConnection.EVENT_KEEPALIVE_STOP_REQUEST, slotId).sendToTarget();
-        waitForMs(100);
-        verify(mSimulatedCommandsVerifier, times(0))
-                .stopNattKeepalive(anyInt(), any(Message.class));
-    }
-
-    @Test
-    @SmallTest
-    public void testStartNattKeepaliveFail() throws Exception {
-        checkStartNattKeepaliveFail(false);
-    }
-
-    @Test
-    @SmallTest
-    public void testStartNattKeepaliveFailCondensed() throws Exception {
-        checkStartNattKeepaliveFail(true);
-    }
-
-    @Test
-    @SmallTest
-    public void testIsUnmeteredUseOnly() throws Exception {
-        Field field = DataConnection.class.getDeclaredField("mTransportType");
-        field.setAccessible(true);
-        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-
-        assertFalse(isUnmeteredUseOnly());
-
-        field = DataConnection.class.getDeclaredField("mTransportType");
-        field.setAccessible(true);
-        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mServiceState).getDataRoaming();
-        doReturn(ApnSetting.TYPE_MMS).when(mApnContext).getApnTypeBitmask();
-
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[] { "default" });
-
-        assertTrue(isUnmeteredUseOnly());
-    }
-
-    @Test
-    public void testIsEnterpriseUse() throws Exception {
-        assertFalse(isEnterpriseUse());
-        assertFalse(mDc.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-
-        setUpDefaultData(1);
-        replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
-        doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
-        doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
-        connectEvent(true);
-
-        assertTrue(isEnterpriseUse());
-        assertTrue(mDc.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
-    }
-
-    @Test
-    @SmallTest
-    public void testGetDisallowedApnTypes() throws Exception {
-        mContextFixture.getCarrierConfigBundle().putStringArray(
-                CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
-                new String[] { "mms", "supl", "fota" });
-        testConnectEvent();
-
-        assertEquals(ApnSetting.TYPE_MMS | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA,
-                getDisallowedApnTypes());
-    }
-
-    @Test
-    public void testIsSuspended() throws Exception {
-        // Return false if not active state
-        assertTrue(mDc.isInactive());
-        assertFalse(isSuspended());
-
-        // Return false for emergency APN
-        doReturn(mApn6).when(mApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_EMERGENCY).when(mApnContext).getApnTypeBitmask();
-        connectEvent(true);
-        assertFalse(isSuspended());
-
-        // Back to DEFAULT APN
-        disconnectEvent();
-        assertTrue(mDc.isInactive());
-        doReturn(mApn1).when(mApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
-        doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
-        connectEvent(true);
-
-        // Before getting any service state event, the connection should not be suspended.
-        assertFalse(isSuspended());
-
-        // Return true if combined reg state is not in service
-        serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
-                ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
-        assertTrue(isSuspended());
-
-        // Return false if in service and concurrent voice and data is allowed
-        serviceStateChangedEvent(ServiceState.STATE_IN_SERVICE,
-                ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
-        assertFalse(isSuspended());
-
-        // Return false if in service and concurrent voice/data not allowed but call state is idle
-        doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed();
-        doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
-        mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
-        waitForMs(100);
-        assertFalse(isSuspended());
-
-        // Return true if in service, concurrent voice/data not allowed, and call state not idle
-        doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
-        mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
-        waitForMs(100);
-        assertTrue(isSuspended());
-    }
-
-    @Test
-    public void testDataCreatedWhenOutOfService() throws Exception {
-        serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
-                ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
-        ArgumentCaptor<NetworkCapabilities> ncCaptor =
-                ArgumentCaptor.forClass(NetworkCapabilities.class);
-        doReturn(mock(Network.class)).when(mConnectivityManager).registerNetworkAgent(
-                any(), any(), any(), ncCaptor.capture(), any(), any(), anyInt());
-
-        doReturn(mApn1).when(mApnContext).getApnSetting();
-        doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
-        doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
-        connectEvent(true);
-        waitForMs(100);
-
-        NetworkCapabilities nc = ncCaptor.getValue();
-        // The network must be created with NOT_SUSPENDED capability.
-        assertTrue(nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED));
-
-        // But it's final state must be suspended.
-        assertTrue(isSuspended());
-    }
-
-    @Test
-    public void testDataServiceTempUnavailable() throws Exception {
-        setFailedSetupDataResponse(DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
-        replaceInstance(ConnectionParams.class, "mRequestType", mCp,
-                DcTracker.REQUEST_TYPE_NORMAL);
-        // Verify that no data was setup
-        connectEvent(false);
-        assertTrue(mDc.isInactive());
-
-        // Verify that data service did not suggest any retry (i.e. Frameworks uses configured
-        // retry timer).
-        verify(mDataThrottler).setRetryTime(eq(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL),
-                eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), eq(DcTracker.REQUEST_TYPE_NORMAL));
-    }
-
-    @Test
-    public void testDataHandoverFailed() throws Exception {
-        doReturn(mDefaultDc).when(mDcTracker).getDataConnectionByApnType(anyString());
-
-        doAnswer(invocation -> {
-            final Consumer<Integer> consumer = (Consumer<Integer>) invocation.getArguments()[0];
-            consumer.accept(DataServiceCallback.RESULT_SUCCESS);
-            return null;
-        }).when(mDefaultDc).startHandover(any(Consumer.class));
-
-        replaceInstance(ConnectionParams.class, "mRequestType", mCp,
-                DcTracker.REQUEST_TYPE_HANDOVER);
-        assertTrue(mDc.isInactive());
-        connectEvent(false);
-
-        // Make sure the data connection is still in inactive state
-        assertTrue(mDc.isInactive());
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
deleted file mode 100644
index ffe4542..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.os.HandlerThread;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.util.Objects;
-
-public class DataEnabledSettingsTest extends TelephonyTest {
-
-    private DataEnabledSettings mDataEnabledSettingsUT;
-
-    private DataEnabledSettingsTestHandler mDataEnabledSettingsTestHandler;
-
-    private String mRules = "";
-
-    private class DataEnabledSettingsTestHandler extends HandlerThread {
-
-        private DataEnabledSettingsTestHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            mDataEnabledSettingsUT = new DataEnabledSettings(mPhone);
-            setReady(true);
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        doReturn(mRules).when(mSubscriptionController).getDataEnabledOverrideRules(anyInt());
-
-        doAnswer(invocation -> {
-            String rules = (String) invocation.getArguments()[1];
-            boolean changed = !Objects.equals(mRules, rules);
-            mRules = rules;
-            return changed;
-        }).when(mSubscriptionController).setDataEnabledOverrideRules(anyInt(), anyString());
-
-        mDataEnabledSettingsTestHandler = new DataEnabledSettingsTestHandler(
-                getClass().getSimpleName());
-        mDataEnabledSettingsTestHandler.start();
-        waitUntilReady();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mDataEnabledSettingsTestHandler.quit();
-        mDataEnabledSettingsTestHandler.join();
-        mDataEnabledSettingsTestHandler = null;
-        mDataEnabledSettingsUT = null;
-        super.tearDown();
-    }
-
-    @Test
-    @SmallTest
-    public void testSetDataAllowedInVoiceCall() throws Exception {
-        mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(true);
-        ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
-        verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
-                stringCaptor.capture());
-        assertEquals("*=nonDefault&inVoiceCall&DefaultDataOn&dsdsEnabled", stringCaptor.getValue());
-
-        clearInvocations(mSubscriptionController);
-
-        mDataEnabledSettingsUT.setAllowDataDuringVoiceCall(false);
-        verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
-                stringCaptor.capture());
-        assertEquals("", stringCaptor.getValue());
-    }
-
-    @Test
-    @SmallTest
-    public void testSetAlwaysAllowMmsData() throws Exception {
-        mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, false);
-        assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(true));
-        ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
-        verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
-                stringCaptor.capture());
-        assertEquals("mms=unconditionally", stringCaptor.getValue());
-        assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
-
-        clearInvocations(mSubscriptionController);
-
-        assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(false));
-        verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
-                stringCaptor.capture());
-        assertEquals("", stringCaptor.getValue());
-        assertFalse(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
-
-        mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, true);
-        assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
-    }
-
-    @Test
-    @SmallTest
-    public void testSetThermalDataEnabled() throws Exception {
-        mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
-                false);
-        assertFalse(mDataEnabledSettingsUT.isDataEnabledForReason(
-                TelephonyManager.DATA_ENABLED_REASON_THERMAL));
-        assertFalse(mDataEnabledSettingsUT.isDataEnabled());
-
-        mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
-                true);
-        assertTrue(mDataEnabledSettingsUT.isDataEnabledForReason(
-                TelephonyManager.DATA_ENABLED_REASON_THERMAL));
-        assertTrue(mDataEnabledSettingsUT.isDataEnabled());
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
deleted file mode 100644
index d03bc8c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/**
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
-import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_NORMAL;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.ThrottleStatus;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Data throttler tests
- */
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DataThrottlerTest extends TelephonyTest {
-
-    private static final boolean DBG = true;
-    private DataThrottler mDataThrottler;
-
-    // Mocked classes
-    private DataThrottler.Callback mMockChangedCallback1;
-    private DataThrottler.Callback mMockChangedCallback2;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        mMockChangedCallback1 = mock(DataThrottler.Callback.class);
-        mMockChangedCallback2 = mock(DataThrottler.Callback.class);
-        mDataThrottler = new DataThrottler(mPhone, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback1);
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mDataThrottler = null;
-        super.tearDown();
-    }
-
-    /**
-     * Test the behavior of a retry manager with no waiting APNs set.
-     */
-    @Test
-    @SmallTest
-    public void testSetRetryTime() throws Exception {
-        final ArgumentCaptor<List<ThrottleStatus>> statusCaptor =
-                ArgumentCaptor.forClass((Class) List.class);
-
-        List<List<ThrottleStatus>> expectedStatuses = new ArrayList<>();
-        processAllMessages();
-        expectedStatuses.add(List.of());
-
-        mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
-                REQUEST_TYPE_NORMAL);
-        processAllMessages();
-        assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
-                mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
-
-        processAllMessages();
-        expectedStatuses.add(List.of(
-                new ThrottleStatus.Builder()
-                    .setSlotIndex(0)
-                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                    .setApnType(ApnSetting.TYPE_DEFAULT)
-                    .setThrottleExpiryTimeMillis(1234567890L)
-                    .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
-                    .build())
-        );
-
-
-        mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, 13579L,
-                REQUEST_TYPE_HANDOVER);
-        processAllMessages();
-        assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
-        assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DUN));
-
-        processAllMessages();
-        expectedStatuses.add(List.of(
-                new ThrottleStatus.Builder()
-                        .setSlotIndex(0)
-                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .setApnType(ApnSetting.TYPE_DUN)
-                        .setThrottleExpiryTimeMillis(13579L)
-                        .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
-                        .build(),
-                new ThrottleStatus.Builder()
-                        .setSlotIndex(0)
-                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .setApnType(ApnSetting.TYPE_DEFAULT)
-                        .setThrottleExpiryTimeMillis(13579L)
-                        .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
-                        .build())
-        );
-
-
-        mDataThrottler.setRetryTime(ApnSetting.TYPE_MMS, -10,
-                REQUEST_TYPE_NORMAL);
-        processAllMessages();
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
-                mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
-        processAllMessages();
-        expectedStatuses.add(List.of(
-                new ThrottleStatus.Builder()
-                        .setSlotIndex(0)
-                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .setNoThrottle()
-                        .setApnType(ApnSetting.TYPE_MMS)
-                        .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
-                        .build()
-        ));
-
-        mDataThrottler.setRetryTime(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_EMERGENCY,
-                RetryManager.NO_RETRY, REQUEST_TYPE_HANDOVER);
-
-        processAllMessages();
-        expectedStatuses.add(List.of(
-                new ThrottleStatus.Builder()
-                        .setSlotIndex(0)
-                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .setApnType(ApnSetting.TYPE_EMERGENCY)
-                        .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
-                        .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
-                        .build(),
-                new ThrottleStatus.Builder()
-                        .setSlotIndex(0)
-                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                        .setApnType(ApnSetting.TYPE_FOTA)
-                        .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
-                        .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
-                        .build()
-        ));
-
-        assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_FOTA));
-        assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_EMERGENCY));
-
-
-        // Loop through statuses and test everything
-        verify(mMockChangedCallback1, times(expectedStatuses.size()))
-                .onThrottleStatusChanged(statusCaptor.capture());
-
-        // Check actual statuses
-        List<List<ThrottleStatus>> actualStatuses =
-                (List<List<ThrottleStatus>>) statusCaptor.getAllValues();
-        assertEquals(expectedStatuses.size(), actualStatuses.size());
-
-        if (DBG) {
-            logd("expectedStatuses.size() = " + expectedStatuses.size());
-            logd("actualStatuses.size() = " + actualStatuses.size());
-        }
-
-        Comparator<ThrottleStatus> comparator = (o1, o2) ->
-                Integer.compare(o1.getApnType(), o2.getApnType());
-
-        for (int i = 0; i < expectedStatuses.size(); i++) {
-            List<ThrottleStatus> atsExpected = new ArrayList<>(expectedStatuses.get(i));
-            List<ThrottleStatus> atsActual = new ArrayList<>(actualStatuses.get(i));
-
-            atsExpected.sort(comparator);
-            atsActual.sort(comparator);
-            assertEquals("Lists at index " + i + " don't match",
-                    atsExpected, atsActual);
-        }
-
-        this.mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback2);
-    }
-
-    /**
-     * Test the behavior of a retry manager with no waiting APNs set.
-     */
-    @Test
-    @SmallTest
-    public void testUnthrottle() throws Exception {
-        mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
-                REQUEST_TYPE_NORMAL);
-        processAllMessages();
-        assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
-
-        mDataThrottler.reset();
-        processAllMessages();
-        assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
-                mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
deleted file mode 100644
index bddfdb4..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.net.InetAddresses;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DcControllerTest extends TelephonyTest {
-
-    private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1;
-    private static final int DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE = 2;
-
-    private static final int EVENT_DATA_STATE_CHANGED = 0x00040007;
-    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 1;
-
-    // Mocked classes
-    private List<ApnContext> mApnContexts;
-    private DataConnection mDc;
-    private DataServiceManager mDataServiceManager;
-    private Handler mTestHandler;
-
-    UpdateLinkPropertyResult mResult;
-
-    private DcController mDcc;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        mApnContexts = mock(List.class);
-        mDc = mock(DataConnection.class);
-        mDataServiceManager = mock(DataServiceManager.class);
-        mTestHandler = mock(Handler.class);
-
-        doReturn("fake.action_detached").when(mPhone).getActionDetached();
-        doReturn(1).when(mApnContexts).size();
-        doReturn(mApnContexts).when(mDc).getApnContexts();
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-
-        LinkProperties lp = new LinkProperties();
-        mResult = new UpdateLinkPropertyResult(lp);
-        doReturn(mResult).when(mDc).updateLinkProperty(any(DataCallResponse.class));
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                .when(mDataServiceManager).getTransportType();
-
-        mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, Looper.myLooper(),
-                "");
-        processAllMessages();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mDcc.removeCallbacksAndMessages(null);
-        mDcc = null;
-        mResult = null;
-        super.tearDown();
-    }
-
-    @Test
-    @SmallTest
-    public void testDataDormant() throws Exception {
-        ArrayList<DataCallResponse> l = new ArrayList<>();
-        DataCallResponse dcResponse = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        l.add(dcResponse);
-
-        mDc.mCid = 1;
-        mDcc.addActiveDcByCid(mDc);
-
-        mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
-                new AsyncResult(null, l, null)));
-        processAllMessages();
-
-        verify(mDcTracker, times(1)).sendStopNetStatPoll(eq(DctConstants.Activity.DORMANT));
-    }
-
-    @Test
-    @SmallTest
-    public void testPhysicalLinkStatusChanged_defaultApnTypeAndDormant_registrantNotifyResult()
-            throws Exception {
-        ArrayList<DataCallResponse> l = new ArrayList<>();
-        DataCallResponse dcResponse = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        l.add(dcResponse);
-        mDc.mCid = 1;
-        mDcc.addActiveDcByCid(mDc);
-        ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
-        List<ApnContext> apnContextList = new ArrayList<>();
-        apnContextList.add(apnContext);
-        doReturn(apnContextList).when(mDc).getApnContexts();
-        doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
-        mDcc.registerForPhysicalLinkStatusChanged(mTestHandler, EVENT_PHYSICAL_LINK_STATUS_CHANGED);
-
-        mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
-                new AsyncResult(null, l, null)));
-        processAllMessages();
-
-        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong());
-        Message message = messageCaptor.getValue();
-        assertEquals(EVENT_PHYSICAL_LINK_STATUS_CHANGED, message.what);
-        AsyncResult ar = (AsyncResult) message.obj;
-        assertEquals(DataCallResponse.LINK_STATUS_DORMANT, (int) ar.result);
-    }
-
-    @Test
-    @SmallTest
-    public void testPhysicalLinkStatusChanged_imsApnTypeAndDormant_NoNotifyResult()
-            throws Exception {
-        testPhysicalLinkStatusChanged_defaultApnTypeAndDormant_registrantNotifyResult();
-
-        ArrayList<DataCallResponse> l = new ArrayList<>();
-        DataCallResponse dcResponse = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        l.add(dcResponse);
-        mDc.mCid = 1;
-        mDcc.addActiveDcByCid(mDc);
-        ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_IMS, TAG, mDcTracker, 1);
-        List<ApnContext> apnContextList = new ArrayList<>();
-        apnContextList.add(apnContext);
-        doReturn(apnContextList).when(mDc).getApnContexts();
-        doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
-
-        mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
-                new AsyncResult(null, l, null)));
-        processAllMessages();
-
-        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mTestHandler, times(1)).sendMessageDelayed(messageCaptor.capture(), anyLong());
-    }
-
-    @Test
-    @SmallTest
-    public void testPhysicalLinkStatusChanged_defaultApnTypeAndStateChanged_registrantNotifyResult()
-            throws Exception {
-        testPhysicalLinkStatusChanged_imsApnTypeAndDormant_NoNotifyResult();
-
-        ArrayList<DataCallResponse> l = new ArrayList<>();
-        DataCallResponse dcResponse = new DataCallResponse.Builder()
-                .setCause(0)
-                .setRetryDurationMillis(-1)
-                .setId(1)
-                .setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_ACTIVE)
-                .setProtocolType(ApnSetting.PROTOCOL_IP)
-                .setInterfaceName(FAKE_IFNAME)
-                .setAddresses(Arrays.asList(
-                        new LinkAddress(InetAddresses.parseNumericAddress(FAKE_ADDRESS), 0)))
-                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_DNS)))
-                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress(FAKE_GATEWAY)))
-                .setPcscfAddresses(
-                        Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
-                .setMtuV4(1440)
-                .setMtuV6(1440)
-                .build();
-        l.add(dcResponse);
-        mDc.mCid = 1;
-        mDcc.addActiveDcByCid(mDc);
-        ApnContext apnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
-        List<ApnContext> apnContextList = new ArrayList<>();
-        apnContextList.add(apnContext);
-        doReturn(apnContextList).when(mDc).getApnContexts();
-        doReturn(true).when(mDcTracker).getLteEndcUsingUserDataForIdleDetection();
-
-        mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
-                new AsyncResult(null, l, null)));
-        processAllMessages();
-
-        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mTestHandler, times(2)).sendMessageDelayed(messageCaptor.capture(), anyLong());
-        Message message = messageCaptor.getValue();
-        assertEquals(EVENT_PHYSICAL_LINK_STATUS_CHANGED, message.what);
-        AsyncResult ar = (AsyncResult) message.obj;
-        assertEquals(DataCallResponse.LINK_STATUS_ACTIVE, (int) ar.result);
-    }
-}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java
deleted file mode 100644
index 78438e4..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcNetworkAgentTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Mockito.doReturn;
-
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkInfo;
-import android.net.NetworkProvider;
-import android.os.Looper;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.lang.reflect.Field;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class DcNetworkAgentTest extends TelephonyTest {
-
-    private DcNetworkAgent mDcNetworkAgent;
-    private DataConnection mDc;
-    private DcController mDcc;
-    private DcFailBringUp mDcFailBringUp;
-
-    private DataServiceManager mDataServiceManager;
-    private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
-    private NetworkProvider mNetworkProvider;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        logd("+Setup!");
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        mDataServiceManager = Mockito.mock(DataServiceManager.class);
-        mDcTesterFailBringUpAll = Mockito.mock(DcTesterFailBringUpAll.class);
-        mNetworkProvider = Mockito.mock(NetworkProvider.class);
-
-        final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
-        configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
-        configBuilder.setLegacyTypeName("MOBILE");
-        configBuilder.setLegacySubType(TelephonyManager.NETWORK_TYPE_LTE);
-        configBuilder.setLegacySubTypeName("LTE");
-        configBuilder.setLegacyExtraInfo("apn");
-
-        doReturn("fake.action_detached").when(mPhone).getActionDetached();
-        mDcFailBringUp = new DcFailBringUp();
-        mDcFailBringUp.saveParameters(0, 0, -2);
-        doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
-
-        mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, Looper.myLooper(),
-                "");
-        mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
-                mDcTesterFailBringUpAll, mDcc);
-
-        LinkProperties linkProperties = new LinkProperties();
-        linkProperties.setInterfaceName("fake_iface");
-        Field field = DataConnection.class.getDeclaredField("mLinkProperties");
-        field.setAccessible(true);
-        field.set(mDc, linkProperties);
-
-        mDcNetworkAgent = new DcNetworkAgent(mDc, mPhone, 45, configBuilder.build(),
-                mNetworkProvider, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        logd("-Setup!");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    private void verifyDisconnected() throws Exception {
-        Field field = NetworkAgent.class.getDeclaredField("mNetworkInfo");
-        field.setAccessible(true);
-        NetworkInfo networkInfo = (NetworkInfo) field.get(mDcNetworkAgent);
-        assertEquals(NetworkInfo.DetailedState.DISCONNECTED, networkInfo.getDetailedState());
-    }
-
-    @Test
-    public void testUnwantedTimeout() throws Exception {
-        mDcNetworkAgent.markConnected();
-        mDcNetworkAgent.onNetworkUnwanted();
-        processAllFutureMessages();
-        verifyDisconnected();
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java
deleted file mode 100644
index b08a680..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcRequestTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.TelephonyNetworkSpecifier;
-import android.telephony.data.ApnSetting;
-
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class DcRequestTest extends TelephonyTest {
-    NetworkRequest mNetworkRequest;
-
-    // Mocked classes
-    ApnConfigTypeRepository mApnConfigTypeRepo;
-
-    @Before
-    public void setUp() throws Exception {
-        mApnConfigTypeRepo = mock(ApnConfigTypeRepository.class);
-        super.setUp(getClass().getSimpleName());
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mNetworkRequest = null;
-        super.tearDown();
-    }
-
-    @Test
-    public void whenNetworkRequestInternetThenPriorityZero() {
-        mNetworkRequest = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .build();
-
-        when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_DEFAULT))
-                .thenReturn(new ApnConfigType(ApnSetting.TYPE_DEFAULT, 0));
-        DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-
-        assertEquals(0, dcRequest.priority);
-    }
-
-    @Test
-    public void whenNetworkRequestMcxThenApnConfigTypeMcxPriorityReturned() {
-        mNetworkRequest = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                //Testing out multiple transport types here
-                .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
-                .build();
-
-        when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_MCX))
-                .thenReturn(new ApnConfigType(ApnSetting.TYPE_MCX, 21));
-        DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-        assertEquals(21, dcRequest.priority);
-    }
-
-    @Test
-    public void whenNetworkRequestNotCellularThenDcRequestIsNull() {
-        mNetworkRequest = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
-                .build();
-        when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_NONE)).thenReturn(null);
-        DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-        assertNull(dcRequest);
-    }
-
-    @Test
-    public void whenNetworkRequestHasNoTransportThenPriorityStays() {
-        //This seems like an invalid case that should be handled differently.
-        mNetworkRequest = new NetworkRequest.Builder()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
-                .build();
-
-        when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_MCX))
-                .thenReturn(new ApnConfigType(ApnSetting.TYPE_MCX, 11));
-        DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-        assertEquals(11, dcRequest.priority);
-    }
-
-    @Test
-    public void whenNetworkRequestNotCellularWithTelephonySpecifierThenDcRequestIsNull() {
-        TelephonyNetworkSpecifier telephonyNetworkSpecifier =
-                new TelephonyNetworkSpecifier.Builder().setSubscriptionId(5).build();
-
-        //This seems like an invalid case that should be handled differently.
-        mNetworkRequest = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
-                .setNetworkSpecifier(telephonyNetworkSpecifier)
-                .build();
-
-        when(mApnConfigTypeRepo.getByType(ApnSetting.TYPE_NONE)).thenReturn(null);
-
-        DcRequest dcRequest = DcRequest.create(mNetworkRequest, mApnConfigTypeRepo);
-
-        assertNull(dcRequest);
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
deleted file mode 100644
index 549c587..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ /dev/null
@@ -1,2983 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ServiceInfo;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.hardware.radio.V1_0.SetupDataCallResult;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkCapabilities;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkRequest;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.provider.Telephony;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.Annotation;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyDisplayInfo;
-import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
-import android.telephony.data.DataProfile;
-import android.telephony.data.DataService;
-import android.telephony.data.TrafficDescriptor;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import androidx.test.filters.FlakyTest;
-
-import com.android.internal.R;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.ISub;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.data.CellularDataService;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.time.Period;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-public class DcTrackerTest extends TelephonyTest {
-    public static final String FAKE_APN1 = "FAKE APN 1";
-    public static final String FAKE_APN2 = "FAKE APN 2";
-    public static final String FAKE_APN3 = "FAKE APN 3";
-    public static final String FAKE_APN4 = "FAKE APN 4";
-    public static final String FAKE_APN5 = "FAKE APN 5";
-    public static final String FAKE_APN6 = "FAKE APN 6";
-    public static final String FAKE_APN7 = "FAKE APN 7";
-    public static final String FAKE_APN8 = "FAKE APN 8";
-    public static final String FAKE_APN9 = "FAKE APN 9";
-    public static final String FAKE_IFNAME = "FAKE IFNAME";
-    public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
-    public static final String FAKE_GATEWAY = "11.22.33.44";
-    public static final String FAKE_DNS = "55.66.77.88";
-    public static final String FAKE_ADDRESS = "99.88.77.66";
-    private static final int NETWORK_TYPE_NR_BITMASK =
-            1 << (TelephonyManager.NETWORK_TYPE_NR - 1);
-    private static final int NETWORK_TYPE_LTE_BITMASK =
-            1 << (TelephonyManager.NETWORK_TYPE_LTE - 1);
-    private static final int NETWORK_TYPE_EHRPD_BITMASK =
-            1 << (TelephonyManager.NETWORK_TYPE_EHRPD - 1);
-    private static final Uri PREFERAPN_URI = Uri.parse(
-            Telephony.Carriers.CONTENT_URI + "/preferapn");
-    private static final int DATA_ENABLED_CHANGED = 0;
-    private static final String FAKE_PLMN = "44010";
-    private static final long TEST_TIMEOUT = 1000;
-
-    // Mocked classes
-    ISub mIsub;
-    IBinder mBinder;
-    SubscriptionInfo mSubscriptionInfo;
-    ApnContext mApnContext;
-    DataConnection mDataConnection;
-    Handler mHandler;
-    NetworkPolicyManager mNetworkPolicyManager;
-
-    private DcTracker mDct;
-    private DcTrackerTestHandler mDcTrackerTestHandler;
-
-    private AlarmManager mAlarmManager;
-
-    private PersistableBundle mBundle;
-
-    private SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
-
-    private final ApnSettingContentProvider mApnSettingContentProvider =
-            new ApnSettingContentProvider();
-
-    private Message mMessage;
-
-    private CellularDataService mCellularDataService;
-
-    private void addDataService() {
-        mCellularDataService = new CellularDataService();
-        ServiceInfo serviceInfo = new ServiceInfo();
-        serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
-        IntentFilter filter = new IntentFilter();
-        mContextFixture.addService(
-                DataService.SERVICE_INTERFACE,
-                null,
-                "com.android.phone",
-                mCellularDataService.mBinder,
-                serviceInfo,
-                filter);
-    }
-
-    private class DcTrackerTestHandler extends HandlerThread {
-
-        private DcTrackerTestHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            mDct = new DcTracker(mPhone, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            setReady(true);
-        }
-    }
-
-    private class ApnSettingContentProvider extends MockContentProvider {
-        public final String[] FAKE_APN_COLUMNS = new String[]{
-                Telephony.Carriers._ID, Telephony.Carriers.NUMERIC,
-                Telephony.Carriers.NAME, Telephony.Carriers.APN,
-                Telephony.Carriers.PROXY, Telephony.Carriers.PORT,
-                Telephony.Carriers.MMSC, Telephony.Carriers.MMSPROXY,
-                Telephony.Carriers.MMSPORT, Telephony.Carriers.USER,
-                Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
-                Telephony.Carriers.TYPE,
-                Telephony.Carriers.PROTOCOL,
-                Telephony.Carriers.ROAMING_PROTOCOL,
-                Telephony.Carriers.CARRIER_ENABLED, Telephony.Carriers.BEARER,
-                Telephony.Carriers.BEARER_BITMASK,
-                Telephony.Carriers.PROFILE_ID,
-                Telephony.Carriers.MODEM_PERSIST,
-                Telephony.Carriers.MAX_CONNECTIONS,
-                Telephony.Carriers.WAIT_TIME_RETRY,
-                Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS,
-                Telephony.Carriers.MTU,
-                Telephony.Carriers.MTU_V4,
-                Telephony.Carriers.MTU_V6,
-                Telephony.Carriers.MVNO_TYPE,
-                Telephony.Carriers.MVNO_MATCH_DATA,
-                Telephony.Carriers.NETWORK_TYPE_BITMASK,
-                Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK,
-                Telephony.Carriers.APN_SET_ID,
-                Telephony.Carriers.CARRIER_ID,
-                Telephony.Carriers.SKIP_464XLAT,
-                Telephony.Carriers.ALWAYS_ON
-        };
-
-        private int mPreferredApnSet = 0;
-
-        private Object[] mPreferredApn = null;
-
-        private String mFakeApn1Types = "default,supl";
-
-        private String mFakeApn5Types = "dun";
-
-        private int mFakeApn1Bitmask = NETWORK_TYPE_LTE_BITMASK;
-
-        private int mRowIdOffset = 0;
-
-        public void setFakeApn1Types(String apnTypes) {
-            mFakeApn1Types = apnTypes;
-        }
-
-        public void setFakeApn5Types(String apnTypes) {
-            mFakeApn5Types = apnTypes;
-        }
-
-        public void setFakeApn1NetworkTypeBitmask(int bitmask) {
-            mFakeApn1Bitmask = bitmask;
-        }
-
-        public void setRowIdOffset(int rowIdOffset) {
-            mRowIdOffset = rowIdOffset;
-        }
-
-        public void setFakePreferredApn(Object[] fakeApn) {
-            mPreferredApn = fakeApn;
-        }
-
-        public Object[] getFakeApn1() {
-            return new Object[]{
-                    2163 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "sp-mode",              // name
-                    FAKE_APN1,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    mFakeApn1Types,         // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    mFakeApn1Bitmask,       // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn2() {
-            return new Object[]{
-                    2164 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "mopera U",             // name
-                    FAKE_APN2,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "default,supl",         // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn3() {
-            return new Object[]{
-                    2165 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "b-mobile for Nexus",   // name
-                    FAKE_APN3,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "ims",                  // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    0,                      // bearer
-                    0,                      // bearer_bitmask
-                    2,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    0,                      // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn4() {
-            return new Object[]{
-                    2166 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "sp-mode ehrpd",        // name
-                    FAKE_APN4,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "default,supl",         // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn5() {
-            return new Object[]{
-                    2167 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "b-mobile for Nexus",   // name
-                    FAKE_APN5,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    mFakeApn5Types,         // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    0,                      // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    0,                      // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn6() {
-            return new Object[]{
-                    2168 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "sp-mode",              // name
-                    FAKE_APN6,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "mms,xcap",             // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn7() {
-            return new Object[]{
-                    2169 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "sp-mode",              // name
-                    FAKE_APN7,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "default",              // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_LTE_BITMASK,  // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    1,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn8() {
-            return new Object[]{
-                    2170 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "IMS",                  // name
-                    FAKE_APN8,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "ims",                  // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
-                    0,                      // bearer_bitmask
-                    2,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    -1,                     // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        public Object[] getFakeApn9() {
-            return new Object[]{
-                    2171 + mRowIdOffset,    // id
-                    FAKE_PLMN,              // numeric
-                    "sp-mode nr",           // name
-                    FAKE_APN9,              // apn
-                    "",                     // proxy
-                    "",                     // port
-                    "",                     // mmsc
-                    "",                     // mmsproxy
-                    "",                     // mmsport
-                    "",                     // user
-                    "",                     // password
-                    -1,                     // authtype
-                    "default,enterprise",   // types
-                    "IP",                   // protocol
-                    "IP",                   // roaming_protocol
-                    1,                      // carrier_enabled
-                    ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
-                    0,                      // bearer_bitmask
-                    0,                      // profile_id
-                    1,                      // modem_cognitive
-                    0,                      // max_conns
-                    0,                      // wait_time
-                    0,                      // max_conns_time
-                    0,                      // mtu
-                    0,                      // mtu_v4
-                    0,                      // mtu_v6
-                    "",                     // mvno_type
-                    "",                     // mnvo_match_data
-                    NETWORK_TYPE_NR_BITMASK, // network_type_bitmask
-                    0,                      // lingering_network_type_bitmask
-                    0,                      // apn_set_id
-                    -1,                     // carrier_id
-                    -1,                     // skip_464xlat
-                    0                       // always_on
-            };
-        }
-
-        @Override
-        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-                            String sortOrder) {
-            logd("ApnSettingContentProvider: query");
-            logd("   uri = " + uri);
-            logd("   projection = " + Arrays.toString(projection));
-            logd("   selection = " + selection);
-            logd("   selectionArgs = " + Arrays.toString(selectionArgs));
-            logd("   sortOrder = " + sortOrder);
-
-            if (uri.compareTo(Telephony.Carriers.CONTENT_URI) == 0
-                    || uri.toString().startsWith(Uri.withAppendedPath(
-                            Telephony.Carriers.CONTENT_URI, "filtered").toString())
-                    || uri.toString().startsWith(Uri.withAppendedPath(
-                            Telephony.Carriers.SIM_APN_URI, "filtered").toString())) {
-                if (projection == null) {
-
-                    logd("Query '" + FAKE_PLMN + "' APN settings");
-                    MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
-                    mc.addRow(getFakeApn1());
-                    mc.addRow(getFakeApn2());
-                    mc.addRow(getFakeApn3());
-                    mc.addRow(getFakeApn4());
-                    mc.addRow(getFakeApn5());
-                    mc.addRow(getFakeApn6());
-                    mc.addRow(getFakeApn7());
-                    mc.addRow(getFakeApn8());
-                    mc.addRow(getFakeApn9());
-
-                    return mc;
-                }
-            } else if (isPathPrefixMatch(uri,
-                    Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapnset"))) {
-                MatrixCursor mc = new MatrixCursor(
-                        new String[]{Telephony.Carriers.APN_SET_ID});
-                // apn_set_id is the only field used with this URL
-                mc.addRow(new Object[]{ mPreferredApnSet });
-                mc.addRow(new Object[]{ 0 });
-                return mc;
-            } else if (isPathPrefixMatch(uri,
-                    Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn_no_update"))) {
-                if (mPreferredApn == null) {
-                    return null;
-                } else {
-                    MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
-                    mc.addRow(mPreferredApn);
-                    return mc;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
-            mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
-            return 1;
-        }
-
-        @Override
-        public int delete(Uri uri, String selection, String[] selectionArgs) {
-            return 0;
-        }
-
-        @Override
-        public Uri insert(Uri uri, ContentValues values) {
-            return null;
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        logd("DcTrackerTest +Setup!");
-        super.setUp(getClass().getSimpleName());
-        mIsub = Mockito.mock(ISub.class);
-        mBinder = Mockito.mock(IBinder.class);
-        mSubscriptionInfo = Mockito.mock(SubscriptionInfo.class);
-        mApnContext = Mockito.mock(ApnContext.class);
-        mDataConnection = Mockito.mock(DataConnection.class);
-        mHandler = Mockito.mock(Handler.class);
-        mNetworkPolicyManager = Mockito.mock(NetworkPolicyManager.class);
-
-        doReturn("fake.action_detached").when(mPhone).getActionDetached();
-        doReturn("fake.action_attached").when(mPhone).getActionAttached();
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
-                .getRilDataRadioTechnology();
-
-        mContextFixture.putStringArrayResource(com.android.internal.R.array
-                .config_mobile_tcp_buffers, new String[]{
-                    "umts:131072,262144,1452032,4096,16384,399360",
-                    "hspa:131072,262144,2441216,4096,16384,399360",
-                    "hsupa:131072,262144,2441216,4096,16384,399360",
-                    "hsdpa:131072,262144,2441216,4096,16384,399360",
-                    "hspap:131072,262144,2441216,4096,16384,399360",
-                    "edge:16384,32768,131072,4096,16384,65536",
-                    "gprs:4096,8192,24576,4096,8192,24576",
-                    "1xrtt:16384,32768,131070,4096,16384,102400",
-                    "evdo:131072,262144,1048576,4096,16384,524288",
-                    "lte:524288,1048576,8388608,262144,524288,4194304"});
-
-        mContextFixture.putResource(R.string.config_wwan_data_service_package,
-                "com.android.phone");
-
-        ((MockContentResolver) mContext.getContentResolver()).addProvider(
-                Telephony.Carriers.CONTENT_URI.getAuthority(), mApnSettingContentProvider);
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
-
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
-                .getPreferredTransport(anyInt());
-        doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
-        doReturn(true).when(mSST).getDesiredPowerState();
-        doReturn(true).when(mSST).getPowerStateFromCarrier();
-        doAnswer(
-                (Answer<Void>) invocation -> {
-                    mOnSubscriptionsChangedListener =
-                            (SubscriptionManager.OnSubscriptionsChangedListener)
-                                    invocation.getArguments()[0];
-                    return null;
-                }
-        ).when(mSubscriptionManager).addOnSubscriptionsChangedListener(any());
-        doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
-        doReturn(mNetworkPolicyManager).when(mContext)
-                .getSystemService(Context.NETWORK_POLICY_SERVICE);
-        doReturn(1).when(mIsub).getDefaultDataSubId();
-        doReturn(mIsub).when(mBinder).queryLocalInterface(anyString());
-        mServiceManagerMockedServices.put("isub", mBinder);
-
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_cell_retries_per_error_code,
-                new String[]{"36,2"});
-
-        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-        mBundle = mContextFixture.getCarrierConfigBundle();
-
-        mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
-
-        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
-        addDataService();
-
-        mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
-        mDcTrackerTestHandler.start();
-        waitUntilReady();
-
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-
-        waitForMs(600);
-        logd("DcTrackerTest -Setup!");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        logd("DcTrackerTest -tearDown");
-        mDct.removeCallbacksAndMessages(null);
-        mDct.stopHandlerThread();
-        mDct = null;
-        mDcTrackerTestHandler.quit();
-        mDcTrackerTestHandler.join();
-        mDcTrackerTestHandler = null;
-        mCellularDataService.onDestroy();
-        mCellularDataService = null;
-        mAlarmManager = null;
-        mBundle = null;
-        mCellularDataService = null;
-        waitForMs(100);
-        super.tearDown();
-    }
-
-    // Create a successful data response
-    private static SetupDataCallResult createSetupDataCallResult() {
-        SetupDataCallResult result = new SetupDataCallResult();
-        result.status = 0;
-        result.suggestedRetryTime = -1;
-        result.cid = 1;
-        result.active = 2;
-        result.type = "IP";
-        result.ifname = FAKE_IFNAME;
-        result.addresses = FAKE_ADDRESS;
-        result.dnses = FAKE_DNS;
-        result.gateways = FAKE_GATEWAY;
-        result.pcscf = FAKE_PCSCF_ADDRESS;
-        result.mtu = 1440;
-        return result;
-    }
-
-    private void verifyDataProfile(DataProfile dp, String apn, int profileId,
-                                   int supportedApnTypesBitmap, int type, int bearerBitmask) {
-        assertEquals(profileId, dp.getProfileId());
-        assertEquals(apn, dp.getApn());
-        assertEquals(ApnSetting.PROTOCOL_IP, dp.getProtocolType());
-        assertEquals(0, dp.getAuthType());
-        assertEquals("", dp.getUserName());
-        assertEquals("", dp.getPassword());
-        assertEquals(type, dp.getType());
-        assertEquals(0, dp.getWaitTime());
-        assertTrue(dp.isEnabled());
-        assertEquals(supportedApnTypesBitmap, dp.getSupportedApnTypesBitmask());
-        assertEquals(ApnSetting.PROTOCOL_IP, dp.getRoamingProtocolType());
-        assertEquals(bearerBitmask, dp.getBearerBitmask());
-        assertEquals(0, dp.getMtu());
-        assertTrue(dp.isPersistent());
-        assertFalse(dp.isPreferred());
-    }
-
-    private void verifyDataConnected(final String apnSetting) {
-        verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
-                any(PendingIntent.class));
-
-        assertEquals(apnSetting, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
-        assertArrayEquals(new String[]{ApnSetting.TYPE_DEFAULT_STRING}, mDct.getActiveApnTypes());
-
-        assertTrue(mDct.isAnyDataConnected());
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-
-        LinkProperties linkProperties = mDct.getLinkProperties(ApnSetting.TYPE_DEFAULT_STRING);
-        assertEquals(FAKE_IFNAME, linkProperties.getInterfaceName());
-        assertEquals(1, linkProperties.getAddresses().size());
-        assertEquals(FAKE_ADDRESS, linkProperties.getAddresses().get(0).getHostAddress());
-        assertEquals(1, linkProperties.getDnsServers().size());
-        assertEquals(FAKE_DNS, linkProperties.getDnsServers().get(0).getHostAddress());
-        assertEquals(FAKE_GATEWAY, linkProperties.getRoutes().get(0).getGateway().getHostAddress());
-    }
-
-    private boolean isHandoverPending(int apnType) {
-        try {
-            Method method = DcTracker.class.getDeclaredMethod("isHandoverPending",
-                    int.class);
-            method.setAccessible(true);
-            return (boolean) method.invoke(mDct, apnType);
-        } catch (Exception e) {
-            fail(e.toString());
-            return false;
-        }
-    }
-
-    private void addHandoverCompleteMsg(Message onCompleteMsg,
-            @Annotation.ApnType int apnType) {
-        try {
-            Method method = DcTracker.class.getDeclaredMethod("addHandoverCompleteMsg",
-                    Message.class, int.class);
-            method.setAccessible(true);
-            method.invoke(mDct, onCompleteMsg, apnType);
-        } catch (Exception e) {
-            fail(e.toString());
-        }
-    }
-
-    private void sendInitializationEvents() {
-        sendCarrierConfigChanged("");
-
-        sendSimStateUpdated("");
-
-        sendEventDataConnectionAttached("");
-
-        waitForMs(200);
-    }
-
-    private void sendCarrierConfigChanged(String messagePrefix) {
-        logd(messagePrefix + "Sending EVENT_CARRIER_CONFIG_CHANGED");
-        mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    private void sendSimStateUpdated(String messagePrefix) {
-        logd(messagePrefix + "Sending EVENT_SIM_STATE_UPDATED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED,
-                TelephonyManager.SIM_STATE_LOADED, 0));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    private void sendEventDataConnectionAttached(String messagePrefix) {
-        logd(messagePrefix + "Sending EVENT_DATA_CONNECTION_ATTACHED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    // Test the unmetered APN setup when data is disabled.
-    @Test
-    @SmallTest
-    public void testTrySetupDataUnmeteredDefaultNotSelected() throws Exception {
-        initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
-        doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mIsub).getDefaultDataSubId();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        sendInitializationEvents();
-
-        mDct.enableApn(ApnSetting.TYPE_XCAP, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the normal data call setup scenario.
-    @Test
-    @MediumTest
-    public void testDataSetup() throws Exception {
-        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-        boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertFalse(dataConnectionReasons.toString(), allowed);
-
-        logd("Sending EVENT_ENABLE_APN");
-        // APN id 0 is APN_TYPE_DEFAULT
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        sendInitializationEvents();
-
-        dataConnectionReasons = new DataConnectionReasons();
-        allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertTrue(dataConnectionReasons.toString(), allowed);
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        verifyDataConnected(FAKE_APN1);
-    }
-
-    // Test the scenario where the first data call setup is failed, and then retry the setup later.
-    @Test
-    @MediumTest
-    public void testDataRetry() throws Exception {
-        AsyncResult ar = new AsyncResult(null,
-                new Pair<>(true, DataEnabledSettings.REASON_USER_DATA_ENABLED), null);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // LOST_CONNECTION(0x10004) is a non-permanent failure, so we'll retry data setup later.
-        SetupDataCallResult result = createSetupDataCallResult();
-        result.status = 0x10004;
-
-        // Simulate RIL fails the data call setup
-        mSimulatedCommands.setDataCallResult(true, result);
-
-        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-        boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertFalse(dataConnectionReasons.toString(), allowed);
-
-        logd("Sending EVENT_ENABLE_APN");
-        // APN id 0 is APN_TYPE_DEFAULT
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        sendInitializationEvents();
-
-        dataConnectionReasons = new DataConnectionReasons();
-        allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertTrue(dataConnectionReasons.toString(), allowed);
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        // This time we'll let RIL command succeed.
-        mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
-
-        //Send event for reconnecting data
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
-                        mPhone.getPhoneId(), DcTracker.REQUEST_TYPE_NORMAL, mApnContext));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        // Verify connected with APN2 setting.
-        verifyDataConnected(FAKE_APN2);
-    }
-
-    @Test
-    @MediumTest
-    @Ignore
-    @FlakyTest
-    public void testUserDisableData() {
-        //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
-        //set Default and MMS to be metered in the CarrierConfigManager
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending DATA_DISABLED_CMD");
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        AsyncResult ar = new AsyncResult(null,
-                new Pair<>(false, DataEnabledSettings.REASON_USER_DATA_ENABLED), null);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-        assertTrue(mDct.isAnyDataConnected());
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
-    }
-
-    @Test
-    @MediumTest
-    public void testTrySetupDataMmsAllowedDataDisabled() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-
-        List<DataProfile> dataProfiles = dpCaptor.getAllValues();
-        assertEquals(2, dataProfiles.size());
-
-        //Verify FAKE_APN1
-        Optional<DataProfile> fakeApn1 = dataProfiles.stream()
-                .filter(dp -> dp.getApn().equals(FAKE_APN1))
-                .findFirst();
-        assertTrue(fakeApn1.isPresent());
-        verifyDataProfile(fakeApn1.get(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        //Verify FAKE_APN6
-        Optional<DataProfile> fakeApn6 = dataProfiles.stream()
-                .filter(dp -> dp.getApn().equals(FAKE_APN6))
-                .findFirst();
-        assertTrue(fakeApn6.isPresent());
-        verifyDataProfile(fakeApn6.get(), FAKE_APN6, 0, ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP,
-                1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending DATA_DISABLED_CMD for default data");
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier, times(2)).deactivateDataCall(
-                anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
-        clearInvocations(mSimulatedCommandsVerifier);
-        doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
-        mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-    }
-
-    @Test
-    @MediumTest
-    public void testTrySetupDataMmsAlwaysAllowedDataDisabled() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mApnSettingContentProvider.setFakeApn1Types("mms,xcap,default");
-        mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-
-        // Verify MMS was set up and is connected
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0,
-                ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP | ApnSetting.TYPE_DEFAULT,
-                1, NETWORK_TYPE_LTE_BITMASK);
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
-        // Verify DC has all capabilities specified in fakeApn1Types
-        Map<Integer, ApnContext> apnContexts = mDct.getApnContexts().stream().collect(
-                Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
-        assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
-        assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_INTERNET));
-
-        // Disable mobile data
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        doReturn(false).when(mDataEnabledSettings).isMmsAlwaysAllowed();
-        mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Expected tear down all metered DataConnections
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier).deactivateDataCall(
-                anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
-        // Allow MMS unconditionally
-        clearInvocations(mSimulatedCommandsVerifier);
-        doReturn(true).when(mDataEnabledSettings).isMmsAlwaysAllowed();
-        doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
-        mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Verify MMS was set up and is connected
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
-
-        // Ensure MMS data connection has the MMS capability only.
-        apnContexts = mDct.getApnContexts().stream().collect(
-                Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
-        assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
-        assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
-                .getNetworkCapabilities().hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_INTERNET));
-    }
-
-    @Test
-    @MediumTest
-    public void testUserDisableRoaming() {
-        //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
-        //step 2: set roaming disabled, data is enabled
-        //step 3: under roaming service
-        //step 4: only tear down metered data connections.
-
-        //set Default and MMS to be metered in the CarrierConfigManager
-        boolean roamingEnabled = mDct.getDataRoamingEnabled();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForHandlerAction(mDct, 1000);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForHandlerAction(mDct, 1000);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        //user is in roaming
-        doReturn(true).when(mServiceState).getDataRoaming();
-        logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataRoamingEnabledByUser(false);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_ROAMING_ON));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-        assertTrue(mDct.isAnyDataConnected());
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
-
-        // reset roaming settings / data enabled settings at end of this test
-        mDct.setDataRoamingEnabledByUser(roamingEnabled);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    @Test
-    @MediumTest
-    public void testDataCallOnUserDisableRoaming() {
-        //step 1: mock under roaming service and user disabled roaming from settings.
-        //step 2: user toggled data settings on
-        //step 3: only non-metered data call is established
-
-        boolean roamingEnabled = mDct.getDataRoamingEnabled();
-        doReturn(true).when(mServiceState).getDataRoaming();
-
-        //set Default and MMS to be metered in the CarrierConfigManager
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataRoamingEnabledByUser(false);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
-
-        assertTrue(mDct.isAnyDataConnected());
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
-
-        // reset roaming settings / data enabled settings at end of this test
-        mDct.setDataRoamingEnabledByUser(roamingEnabled);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    // Test the default data switch scenario.
-    @FlakyTest /* flakes 1.57% of the time */
-    @Test
-    @MediumTest
-    public void testDDSResetAutoAttach() throws Exception {
-        mContextFixture.putBooleanResource(
-                com.android.internal.R.bool.config_auto_attach_data_on_creation, true);
-        testDataSetup();
-        assertTrue(mDct.shouldAutoAttach());
-        mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        // The auto attach flag should be reset after update
-        assertFalse(mDct.shouldAutoAttach());
-    }
-
-    // Test for API carrierActionSetMeteredApnsEnabled.
-    @FlakyTest
-    @Ignore
-    @Test
-    @MediumTest
-    public void testCarrierActionSetMeteredApnsEnabled() {
-        //step 1: setup two DataCalls one for Internet and IMS
-        //step 2: set data is enabled
-        //step 3: cold sim is detected
-        //step 4: all data connection is torn down
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
-        assertTrue(mDct.isAnyDataConnected());
-
-        AsyncResult ar = new AsyncResult(null,
-                new Pair<>(false, DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER), null);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_CHANGED, ar));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // Validate all metered data connections have been torn down
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-        assertTrue(mDct.isAnyDataConnected());
-        assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-    }
-
-    private void initApns(String targetApn, String[] canHandleTypes) {
-        doReturn(targetApn).when(mApnContext).getApnType();
-        doReturn(ApnSetting.getApnTypesBitmaskFromString(mApnContext.getApnType()))
-                .when(mApnContext).getApnTypeBitmask();
-        doReturn(true).when(mApnContext).isConnectable();
-        ApnSetting apnSetting = createApnSetting(ApnSetting.getApnTypesBitmaskFromString(
-                TextUtils.join(",", canHandleTypes)));
-        doReturn(apnSetting).when(mApnContext).getNextApnSetting();
-        doReturn(apnSetting).when(mApnContext).getApnSetting();
-        doReturn(mDataConnection).when(mApnContext).getDataConnection();
-        doReturn(true).when(mApnContext).isEnabled();
-        doReturn(true).when(mApnContext).isDependencyMet();
-        doReturn(true).when(mApnContext).isReady();
-        doReturn(false).when(mApnContext).hasRestrictedRequests(eq(true));
-    }
-
-    // Test the emergency APN setup.
-    @Test
-    @SmallTest
-    public void testTrySetupDataEmergencyApn() {
-        initApns(ApnSetting.TYPE_EMERGENCY_STRING,
-                new String[]{ApnSetting.TYPE_EMERGENCY_STRING});
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        waitForMs(200);
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
-                anyBoolean(), any(Message.class));
-    }
-
-    // Test the XCAP APN setup.
-    @Test
-    @SmallTest
-    public void testTrySetupDataXcapApn() {
-        initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
-        mDct.enableApn(ApnSetting.TYPE_XCAP, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
-                anyBoolean(), any(Message.class));
-    }
-
-    // Test the ENTERPRISE APN setup.
-    @Test
-    public void testTrySetupDataEnterpriseApn() {
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
-        assertEquals(null, tdCaptor.getValue().getOsAppId());
-
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        SetupDataCallResult result = createSetupDataCallResult();
-        result.cid = 10;
-        mSimulatedCommands.setDataCallResult(true, result);
-        mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForMs(200);
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-        assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                tdCaptor.getValue().getOsAppId()));
-    }
-
-    // Test the ENTERPRISE APN setup when default data is not set up yet.
-    @Test
-    public void testTrySetupDataEnterpriseApnNoDefaultData() {
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-        assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                tdCaptor.getValue().getOsAppId()));
-
-        // Check APN contexts with no DEFAULT set up
-        Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertEquals(DctConstants.State.IDLE, apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
-        assertEquals(DctConstants.State.FAILED,
-                apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        waitForMs(200);
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
-        assertEquals(null, tdCaptor.getValue().getOsAppId());
-
-        // Check APN contexts after DEFAULT is set up (and ENTERPRISE failure)
-        apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertEquals(DctConstants.State.CONNECTED,
-                apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
-        assertEquals(DctConstants.State.FAILED,
-                apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        SetupDataCallResult result = createSetupDataCallResult();
-        result.cid = 10;
-        mSimulatedCommands.setDataCallResult(true, result);
-        mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        waitForMs(200);
-
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-        assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                tdCaptor.getValue().getOsAppId()));
-
-        // Check APN contexts after DEFAULT is set up (and ENTERPRISE reenabled)
-        apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertEquals(DctConstants.State.CONNECTED,
-                apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
-        assertEquals(DctConstants.State.CONNECTED,
-                apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-    }
-
-    // Test the ENTERPRISE APN setup when the same CID is returned.
-    @Test
-    public void testTrySetupDataEnterpriseApnDuplicateCid() {
-        mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
-                NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        // mSimulatedCommandsVerifier will return the same CID in SetupDataCallResult
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-        waitForMs(200);
-
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        List<TrafficDescriptor> tds = tdCaptor.getAllValues();
-        // [0] is default and [1] is enterprise, since default should be set up first
-        assertEquals(FAKE_APN1, tds.get(0).getDataNetworkName());
-        assertEquals(null, tds.get(0).getOsAppId());
-        assertEquals(null, tds.get(1).getDataNetworkName());
-        assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(), tds.get(1).getOsAppId()));
-
-        // Check APN contexts after DEFAULT and ENTERPRISE set up
-        Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertEquals(DctConstants.State.CONNECTED,
-                apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
-        assertEquals(DctConstants.State.FAILED,
-                apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
-    }
-
-    @Test
-    @SmallTest
-    public void testGetDataConnectionState() {
-        initApns(ApnSetting.TYPE_SUPL_STRING,
-                new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_DEFAULT_STRING});
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        // Assert that both APN_TYPE_SUPL & APN_TYPE_DEFAULT are connected even we only setup data
-        // for APN_TYPE_SUPL
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_SUPL_STRING));
-        assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
-    }
-
-    // Test the unmetered APN setup when data is disabled.
-    @Test
-    @SmallTest
-    public void testTrySetupDataUnmeteredDataDisabled() {
-        initApns(ApnSetting.TYPE_SUPL_STRING, new String[]{ApnSetting.TYPE_SUPL_STRING});
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_FOTA_STRING});
-
-        mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the unmetered default APN setup when data is disabled. Default APN should always honor
-    // the users's setting.
-    @Test
-    @SmallTest
-    public void testTrySetupDataUnmeteredDefaultDataDisabled() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_MMS_STRING});
-
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, never()).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-
-    // Test the metered APN setup when data is disabled.
-    @Test
-    @SmallTest
-    public void testTrySetupMeteredDataDisabled() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the restricted data request when data is disabled.
-    @Test
-    @SmallTest
-    public void testTrySetupRestrictedDataDisabled() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        sendInitializationEvents();
-
-        NetworkRequest nr = new NetworkRequest.Builder()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .build();
-        mDct.requestNetwork(nr, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the restricted data request when roaming is disabled.
-    @Test
-    @SmallTest
-    public void testTrySetupRestrictedRoamingDisabled() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        mDct.setDataRoamingEnabledByUser(false);
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        //user is in roaming
-        doReturn(true).when(mServiceState).getDataRoaming();
-
-        sendInitializationEvents();
-
-        NetworkRequest nr = new NetworkRequest.Builder()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .build();
-        mDct.requestNetwork(nr, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the default data when data is not connectable.
-    @Test
-    @SmallTest
-    public void testTrySetupNotConnectable() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        doReturn(false).when(mApnContext).isConnectable();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the default data on IWLAN.
-    @Test
-    @SmallTest
-    public void testTrySetupDefaultOnIWLAN() {
-        doReturn(true).when(mAccessNetworksManager).isInLegacyMode();
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test the default data when the phone is in ECBM.
-    @Test
-    @SmallTest
-    public void testTrySetupDefaultInECBM() {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        doReturn(true).when(mPhone).isInEcm();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-
-        sendInitializationEvents();
-
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    // Test update waiting apn list when on data rat change
-    @FlakyTest /* flakes 0.86% of the time */
-    @Ignore
-    @Test
-    @SmallTest
-    public void testUpdateWaitingApnListOnDataRatChange() {
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
-        assertTrue(mDct.isAnyDataConnected());
-
-        //data rat change from ehrpd to lte
-        logd("Sending EVENT_DATA_RAT_CHANGED");
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // Verify the disconnected data call due to rat change and retry manger schedule another
-        // data call setup
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-        verify(mAlarmManager, times(1)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
-                anyLong(), any(PendingIntent.class));
-
-        //Send event for reconnecting data
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
-                        mPhone.getPhoneId(), DcTracker.RELEASE_TYPE_NORMAL, mApnContext));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-        assertTrue(mDct.isAnyDataConnected());
-    }
-
-    /**
-     * Test that fetchDunApns() returns list that prioritize the preferred APN when the preferred
-     * APN including DUN type.
-     */
-    @Test
-    public void testFetchDunApnWithPreferredApn() {
-        // Set support APN types of FAKE_APN1 and FAKE_APN5
-        mApnSettingContentProvider.setFakeApn1Types("default,dun");
-        mApnSettingContentProvider.setFakeApn5Types("default,dun");
-
-        // Set prefer apn set id.
-        ContentResolver cr = mContext.getContentResolver();
-        ContentValues values = new ContentValues();
-        values.put(Telephony.Carriers.APN_SET_ID, 0);
-        cr.update(PREFERAPN_URI, values, null, null);
-        // Set FAKE_APN5 as the preferred APN.
-        mApnSettingContentProvider.setFakePreferredApn(mApnSettingContentProvider.getFakeApn5());
-
-        sendInitializationEvents();
-
-        // Return the APN list that set the preferred APN at the top.
-        ArrayList<ApnSetting> dunApns = mDct.fetchDunApns();
-        assertEquals(2, dunApns.size());
-        assertEquals(FAKE_APN5, dunApns.get(0).getApnName());
-        assertEquals(FAKE_APN1, dunApns.get(1).getApnName());
-    }
-
-    // This tests simulates the race case where the sim status change event is triggered, the
-    // default data connection is attached, and then the carrier config gets changed which bumps
-    // the database id which we want to ignore when cleaning up connections and matching against
-    // the dun APN.  Tests b/158908392.
-    @Test
-    @SmallTest
-    public void testCheckForCompatibleDataConnectionWithDunWhenIdsChange() {
-        //Set dun as a support apn type of FAKE_APN1
-        mApnSettingContentProvider.setFakeApn1Types("default,supl,dun");
-
-        // Enable the default apn
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        //Load the sim and attach the data connection without firing the carrier changed event
-        final String logMsgPrefix = "testCheckForCompatibleDataConnectionWithDunWhenIdsChange: ";
-        sendSimStateUpdated(logMsgPrefix);
-        sendEventDataConnectionAttached(logMsgPrefix);
-        waitForMs(200);
-
-        // Confirm that FAKE_APN1 comes up as a dun candidate
-        ApnSetting dunApn = mDct.fetchDunApns().get(0);
-        assertEquals(dunApn.getApnName(), FAKE_APN1);
-        Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
-        //Double check that the default apn content is connected while the dun apn context is not
-        assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
-                DctConstants.State.CONNECTED);
-        assertNotEquals(apnContexts.get(ApnSetting.TYPE_DUN).getState(),
-                DctConstants.State.CONNECTED);
-
-
-        //Change the row ids the same way as what happens when we have old apn values in the
-        //carrier table
-        mApnSettingContentProvider.setRowIdOffset(100);
-        sendCarrierConfigChanged(logMsgPrefix);
-        waitForMs(200);
-
-        mDct.enableApn(ApnSetting.TYPE_DUN, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
-        //Make sure that the data connection used earlier wasn't cleaned up and still in use.
-        assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
-                apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
-
-        //Check that the DUN is using the same active data connection
-        assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
-                apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DUN).getDataConnection());
-    }
-
-    @Test
-    @SmallTest
-    public void testCheckForCompatibleDataConnectionWithEnterprise() {
-        // Allow both DEFAULT and ENTERPRISE to use APN 1
-        mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
-                NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
-
-        // Enable the DEFAULT APN
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        sendInitializationEvents();
-
-        ArgumentCaptor<TrafficDescriptor> tdCaptor =
-                ArgumentCaptor.forClass(TrafficDescriptor.class);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
-        assertEquals(null, tdCaptor.getValue().getOsAppId());
-
-        // Check APN contexts after DEFAULT is set up
-        Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-        assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
-                DctConstants.State.CONNECTED);
-        assertNotEquals(apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState(),
-                DctConstants.State.CONNECTED);
-
-        // Enable the ENTERPRISE APN
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        SetupDataCallResult result = createSetupDataCallResult();
-        result.cid = 10;
-        mSimulatedCommands.setDataCallResult(true, result);
-        mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForMs(200);
-
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
-                eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
-                anyBoolean(), any(Message.class));
-        assertEquals(null, tdCaptor.getValue().getDataNetworkName());
-        assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
-                tdCaptor.getValue().getOsAppId()));
-
-        // Check APN contexts after ENTERPRISE is set up
-        Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
-                .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
-
-        // Make sure that the data connection used earlier wasn't cleaned up and still in use.
-        assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
-                apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
-
-        // Check that ENTERPRISE isn't using the same data connection as DEFAULT
-        assertNotEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
-                apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_ENTERPRISE).getDataConnection());
-    }
-
-    // Test for Data setup with APN Set ID
-    @Test
-    @SmallTest
-    public void testDataSetupWithApnSetId() throws Exception {
-        // Set the prefer apn set id to "1"
-        ContentResolver cr = mContext.getContentResolver();
-        ContentValues values = new ContentValues();
-        values.put(Telephony.Carriers.APN_SET_ID, 1);
-        cr.update(PREFERAPN_URI, values, null, null);
-
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-
-        List<DataProfile> dataProfiles = dpCaptor.getAllValues();
-        assertEquals(2, dataProfiles.size());
-
-        // Verify to use FAKE APN7 which is Default APN with apnSetId=1(Same as the pereferred
-        // APN's set id).
-        Optional<DataProfile> fakeApn7 = dataProfiles.stream()
-                .filter(dp -> dp.getApn().equals(FAKE_APN7)).findFirst();
-        assertTrue(fakeApn7.isPresent());
-        verifyDataProfile(fakeApn7.get(), FAKE_APN7, 0, 17, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        // Verify to use FAKE APN8 which is IMS APN with apnSetId=-1
-        // (Telephony.Carriers.MATCH_ALL_APN_SET_ID).
-        Optional<DataProfile> fakeApn8 = dataProfiles.stream()
-                .filter(dp -> dp.getApn().equals(FAKE_APN8)).findFirst();
-        assertTrue(fakeApn8.isPresent());
-        verifyDataProfile(fakeApn8.get(), FAKE_APN8, 2, 64, 1, NETWORK_TYPE_LTE_BITMASK);
-    }
-
-    // Test oos
-    @Test
-    @SmallTest
-    public void testDataRatChangeOOS() {
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING});
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
-        assertTrue(mDct.isAnyDataConnected());
-
-        // Data rat change from ehrpd to unknown due to OOS
-        logd("Sending EVENT_DATA_RAT_CHANGED");
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UNKNOWN)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // Verify data connection is on
-        verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-
-        // Data rat resume from unknown to ehrpd
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Verify the same data connection
-        assertEquals(FAKE_APN4, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
-        assertTrue(mDct.isAnyDataConnected());
-    }
-
-    // Test provisioning
-    /*@Test
-    @SmallTest
-    public void testDataEnableInProvisioning() throws Exception {
-        ContentResolver resolver = mContext.getContentResolver();
-
-        assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
-        assertTrue(mDct.isDataEnabled());
-        assertTrue(mDct.isUserDataEnabled());
-
-        mDct.setUserDataEnabled(false);
-        waitForMs(200);
-
-        assertEquals(0, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
-        assertFalse(mDct.isDataEnabled());
-        assertFalse(mDct.isUserDataEnabled());
-
-        // Changing provisioned to 0.
-        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
-        waitForMs(200);
-
-        assertTrue(mDct.isDataEnabled());
-        assertTrue(mDct.isUserDataEnabled());
-
-        // Enable user data during provisioning. It should write to
-        // Settings.Global.MOBILE_DATA and keep data enabled when provisioned.
-        mDct.setUserDataEnabled(true);
-        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
-        waitForMs(200);
-
-        assertTrue(mDct.isDataEnabled());
-        assertTrue(mDct.isUserDataEnabled());
-        assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
-    }*/
-
-    /*
-    @Test
-    @SmallTest
-    public void testNotifyDataEnabledChanged() throws Exception {
-        doAnswer(invocation -> {
-            mMessage = (Message) invocation.getArguments()[0];
-            return true;
-        }).when(mHandler).sendMessageDelayed(any(), anyLong());
-
-        // Test registration.
-        mDct.registerForDataEnabledChanged(mHandler, DATA_ENABLED_CHANGED, null);
-        verifyDataEnabledChangedMessage(true, DataEnabledSettings.REASON_REGISTERED);
-
-        // Disable user data. Should receive data enabled change to false.
-        mDct.setUserDataEnabled(false);
-        waitForMs(200);
-        verifyDataEnabledChangedMessage(false, DataEnabledSettings.REASON_USER_DATA_ENABLED);
-
-        // Changing provisioned to 0. Shouldn't receive any message, as data enabled remains false.
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
-        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
-                0);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
-        waitForMs(200);
-        assertFalse(mDct.isDataEnabled());
-        verify(mHandler, never()).sendMessageDelayed(any(), anyLong());
-
-        // Changing provisioningDataEnabled to 1. It should trigger data enabled change to true.
-        Settings.Global.putInt(resolver,
-                Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
-        mDct.sendMessage(mDct.obtainMessage(
-                DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE, null));
-        waitForMs(200);
-        verifyDataEnabledChangedMessage(
-                true, DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED);
-    }*/
-
-    @Test
-    @SmallTest
-    public void testNetworkStatusChangedRecoveryOFF() {
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.VALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.INVALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        waitForMs(200);
-
-        // Verify that its no-op when the new data stall detection feature is disabled
-        verify(mSimulatedCommandsVerifier, times(0)).getDataCallList(any(Message.class));
-    }
-
-    @FlakyTest
-    @Test
-    @SmallTest
-    public void testNetworkStatusChangedRecoveryON() {
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
-        Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 0);
-        doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.VALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.INVALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).getDataCallList(any(Message.class));
-    }
-
-    @FlakyTest
-    @Test
-    @SmallTest
-    public void testRecoveryStepPDPReset() {
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
-        Settings.Global.putLong(resolver,
-                Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
-        Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 1);
-        doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.INVALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        waitForMs(200);
-
-        // expected tear down all DataConnections
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-    }
-
-
-    @Test
-    @SmallTest
-    public void testRecoveryStepReRegister() {
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
-        Settings.Global.putLong(resolver,
-                Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
-        Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 2);
-        doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-        doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.INVALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // expected to get preferred network type
-        verify(mSST, times(1)).reRegisterNetwork(eq(null));
-    }
-
-    @Test
-    @SmallTest
-    public void testRecoveryStepRestartRadio() {
-        ContentResolver resolver = mContext.getContentResolver();
-        Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
-        Settings.Global.putLong(resolver,
-                Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
-        Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 3);
-        doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
-        doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
-                new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NETWORK_STATUS_CHANGED,
-                NetworkAgent.INVALID_NETWORK, 1, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // expected to get preferred network type
-        verify(mSST, times(1)).powerOffRadioSafely();
-    }
-
-    private void verifyDataEnabledChangedMessage(boolean enabled, int reason) {
-        verify(mHandler, times(1)).sendMessageDelayed(any(), anyLong());
-        Pair<Boolean, Integer> result = (Pair) ((AsyncResult) mMessage.obj).result;
-        assertEquals(DATA_ENABLED_CHANGED, mMessage.what);
-        assertEquals(enabled, result.first);
-        assertEquals(reason, (int) result.second);
-        clearInvocations(mHandler);
-    }
-
-    private void setUpSubscriptionPlans(boolean isNrUnmetered) throws Exception {
-        List<SubscriptionPlan> plans = new ArrayList<>();
-        if (isNrUnmetered) {
-            plans.add(SubscriptionPlan.Builder
-                    .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
-                            Period.ofMonths(1))
-                    .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
-                            SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
-                    .setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_NR})
-                    .build());
-        }
-        plans.add(SubscriptionPlan.Builder
-                .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
-                        Period.ofMonths(1))
-                .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
-                .setDataUsage(500_000_000, System.currentTimeMillis())
-                .build());
-        replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, plans);
-        doReturn(plans.toArray(new SubscriptionPlan[0])).when(mNetworkPolicyManager)
-                .getSubscriptionPlans(anyInt(), any());
-    }
-
-    private void resetSubscriptionPlans() throws Exception {
-        replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, null);
-    }
-
-    private void setUpSubscriptionOverride(int[] networkTypes, boolean isUnmetered)
-            throws Exception {
-        List<Integer> networkTypesList = null;
-        if (networkTypes != null) {
-            networkTypesList = new ArrayList<>();
-            for (int networkType : networkTypes) {
-                networkTypesList.add(networkType);
-            }
-        }
-        replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, networkTypesList);
-        replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, isUnmetered);
-    }
-
-    private void resetSubscriptionOverride() throws Exception {
-        replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, null);
-        replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, false);
-    }
-
-    private boolean isNetworkTypeUnmetered(int networkType) throws Exception {
-        Method method = DcTracker.class.getDeclaredMethod(
-                "isNetworkTypeUnmetered", int.class);
-        method.setAccessible(true);
-        return (boolean) method.invoke(mDct, networkType);
-    }
-
-    private int setUpDataConnection() throws Exception {
-        Field dc = DcTracker.class.getDeclaredField("mDataConnections");
-        dc.setAccessible(true);
-        Field uig = DcTracker.class.getDeclaredField("mUniqueIdGenerator");
-        uig.setAccessible(true);
-        int id = ((AtomicInteger) uig.get(mDct)).getAndIncrement();
-        ((HashMap<Integer, DataConnection>) dc.get(mDct)).put(id, mDataConnection);
-        return id;
-    }
-
-    private void resetDataConnection(int id) throws Exception {
-        Field dc = DcTracker.class.getDeclaredField("mDataConnections");
-        dc.setAccessible(true);
-        ((HashMap<Integer, DataConnection>) dc.get(mDct)).remove(id);
-    }
-
-    private void setUpWatchdogTimer() {
-        // Watchdog active for 10s
-        mBundle.putLong(CarrierConfigManager.KEY_5G_WATCHDOG_TIME_MS_LONG, 10000);
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    private boolean getWatchdogStatus() throws Exception {
-        Field field = DcTracker.class.getDeclaredField(("mWatchdog"));
-        field.setAccessible(true);
-        return (boolean) field.get(mDct);
-    }
-
-    private Map<Integer, List<Message>> getHandoverCompletionMessages() throws Exception {
-        Field field = DcTracker.class.getDeclaredField(("mHandoverCompletionMsgs"));
-        field.setAccessible(true);
-        return (Map<Integer, List<Message>>) field.get(mDct);
-    }
-
-    private void setUpTempNotMetered() {
-        doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
-                .when(mPhone).getRadioAccessFamily();
-        doReturn(1).when(mPhone).getSubId();
-        mBundle.putBoolean(CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    @Test
-    public void testIsNetworkTypeUnmetered() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
-        // only 5G unmetered
-        setUpSubscriptionOverride(new int[]{TelephonyManager.NETWORK_TYPE_NR}, true);
-
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        // all network types metered
-        setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), false);
-
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        // all network types unmetered
-        setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), true);
-
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        resetSubscriptionOverride();
-    }
-
-    @Test
-    public void testIsNetworkTypeUnmeteredViaSubscriptionPlans() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-
-        // only 5G unmetered
-        setUpSubscriptionPlans(true);
-
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        // all network types metered
-        setUpSubscriptionPlans(false);
-
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        // all network types unmetered
-        List<SubscriptionPlan> plans = new ArrayList<>();
-        plans.add(SubscriptionPlan.Builder
-                .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
-                        Period.ofMonths(1))
-                .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
-                        SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
-                .build());
-        replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, plans);
-
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
-        assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
-
-        resetSubscriptionPlans();
-    }
-
-    @Test
-    public void testIsNrUnmeteredSubscriptionPlans() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        int id = setUpDataConnection();
-        setUpSubscriptionPlans(false);
-        setUpWatchdogTimer();
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        setUpTempNotMetered();
-        clearInvocations(mDataConnection);
-
-        // NetCapability should be metered when connected to 5G with no unmetered plan or frequency
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
-        // Set SubscriptionPlans unmetered
-        setUpSubscriptionPlans(true);
-
-        // NetCapability should switch to unmetered with an unmetered plan
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
-        // Set MMWAVE frequency to unmetered
-        mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        clearInvocations(mDataConnection);
-
-        // NetCapability should switch to metered without fr=MMWAVE
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
-        // NetCapability should switch to unmetered with fr=MMWAVE
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
-        resetDataConnection(id);
-        resetSubscriptionPlans();
-    }
-
-    @Test
-    public void testIsNrUnmeteredCarrierConfigs() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        int id = setUpDataConnection();
-        setUpSubscriptionPlans(false);
-        setUpWatchdogTimer();
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        setUpTempNotMetered();
-        clearInvocations(mDataConnection);
-
-        // NetCapability should be metered when connected to 5G with no unmetered plan or frequency
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
-        // Set MMWAVE frequency to unmetered
-        mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL, true);
-        mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        clearInvocations(mDataConnection);
-
-        // NetCapability should switch to unmetered when fr=MMWAVE and MMWAVE unmetered
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
-        // NetCapability should switch to metered when fr=SUB6 and MMWAVE unmetered
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
-        // Set SUB6 frequency to unmetered
-        doReturn(2).when(mPhone).getSubId();
-        mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
-        mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL, true);
-        intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        clearInvocations(mDataConnection);
-
-        // NetCapability should switch to unmetered when fr=SUB6 and SUB6 unmetered
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
-        resetDataConnection(id);
-        resetSubscriptionPlans();
-    }
-
-    @Test
-    public void testReevaluateUnmeteredConnectionsOnNetworkChange() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        int id = setUpDataConnection();
-        setUpSubscriptionPlans(true);
-        setUpWatchdogTimer();
-        setUpTempNotMetered();
-        clearInvocations(mDataConnection);
-
-        // NetCapability should be unmetered when connected to 5G
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        verify(mDataConnection, times(1)).onMeterednessChanged(true);
-
-        // NetCapability should be metered when disconnected from 5G
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        verify(mDataConnection, times(1)).onMeterednessChanged(false);
-
-        resetDataConnection(id);
-        resetSubscriptionPlans();
-    }
-
-    @Test
-    public void testReevaluateUnmeteredConnectionsOnWatchdog() throws Exception {
-        initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
-        int id = setUpDataConnection();
-        setUpSubscriptionPlans(true);
-        setUpWatchdogTimer();
-
-        // Watchdog inactive when unmetered and not connected to 5G
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_NR_TIMER_WATCHDOG));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        assertFalse(getWatchdogStatus());
-
-        // Watchdog active when unmetered and connected to 5G
-        doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
-                .when(mDisplayInfoController).getTelephonyDisplayInfo();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        assertTrue(getWatchdogStatus());
-
-        // Watchdog inactive when metered
-        setUpSubscriptionPlans(false);
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        assertFalse(getWatchdogStatus());
-
-        resetDataConnection(id);
-        resetSubscriptionPlans();
-    }
-
-    /**
-     * Test if this is a path prefix match against the given Uri. Verifies that
-     * scheme, authority, and atomic path segments match.
-     *
-     * Copied from frameworks/base/core/java/android/net/Uri.java
-     */
-    private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
-        if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
-        if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
-
-        List<String> segA = uriA.getPathSegments();
-        List<String> segB = uriB.getPathSegments();
-
-        final int size = segB.size();
-        if (segA.size() < size) return false;
-
-        for (int i = 0; i < size; i++) {
-            if (!Objects.equals(segA.get(i), segB.get(i))) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    @Test
-    public void testNoApnContextsWhenDataIsDisabled() throws java.lang.InterruptedException {
-        //Check that apn contexts are loaded.
-        assertTrue(mDct.getApnContexts().size() > 0);
-
-        //Do work normally done in teardown.
-        mDct.removeCallbacksAndMessages(null);
-        mDcTrackerTestHandler.quit();
-        mDcTrackerTestHandler.join();
-
-        //Set isDataCapable to false for the new DcTracker being created in DcTrackerTestHandler.
-        doReturn(false).when(mTelephonyManager).isDataCapable();
-        mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
-        setReady(false);
-
-        mDcTrackerTestHandler.start();
-        waitUntilReady();
-        assertEquals(0, mDct.getApnContexts().size());
-
-        //No need to clean up handler because that work is done in teardown.
-    }
-
-    @Test
-    public void testRatChanged() throws Exception {
-        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-        boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertFalse(dataConnectionReasons.toString(), allowed);
-
-        logd("Sending EVENT_ENABLE_APN");
-        // APN id 0 is APN_TYPE_DEFAULT
-        mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
-
-        sendInitializationEvents();
-
-        dataConnectionReasons = new DataConnectionReasons();
-        allowed = mDct.isDataAllowed(dataConnectionReasons);
-        assertTrue(dataConnectionReasons.toString(), allowed);
-
-        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
-        // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
-                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
-
-        verifyDataConnected(FAKE_APN1);
-
-        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS).when(mServiceState)
-                .getRilDataRadioTechnology();
-
-        logd("Sending EVENT_DATA_RAT_CHANGED");
-        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
-                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                .build();
-        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
-                anyInt(), anyInt());
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Data connection is running on a different thread. Have to wait.
-        waitForMs(200);
-        // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier).deactivateDataCall(
-                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
-                any(Message.class));
-    }
-
-    @Test
-    public void testApnConfigRepositoryUpdatedOnCarrierConfigChange() {
-        assertPriority(ApnSetting.TYPE_CBS_STRING, 2);
-        assertPriority(ApnSetting.TYPE_MMS_STRING, 2);
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
-                new String[] {
-                        ApnSetting.TYPE_CBS_STRING + ":11",
-                        ApnSetting.TYPE_MMS_STRING + ":19",
-                });
-
-        sendInitializationEvents();
-
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
-        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        assertPriority(ApnSetting.TYPE_CBS_STRING, 11);
-        assertPriority(ApnSetting.TYPE_MMS_STRING, 19);
-
-        //Ensure apns are in sorted order.
-        ApnContext lastApnContext = null;
-        for (ApnContext apnContext : mDct.getApnContexts()) {
-            if (lastApnContext != null) {
-                assertTrue(apnContext.getPriority() <= lastApnContext.getPriority());
-            }
-            lastApnContext = apnContext;
-        }
-    }
-
-    private void assertPriority(String type, int priority) {
-        assertEquals(priority, mDct.getApnContexts().stream()
-                .filter(x -> x.getApnType().equals(type))
-                .findFirst().get().getPriority());
-    }
-
-    @Test
-    public void testProvisionBroadcastReceiver() {
-        Intent intent = new Intent("com.android.internal.telephony.PROVISION");
-        intent.putExtra("provision.phone.id", mPhone.getPhoneId());
-        try {
-            mContext.sendBroadcast(intent);
-        } catch (SecurityException e) {
-            fail();
-        }
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-    }
-
-    @Test
-    public void testRetryHandoverWhenDisconnecting() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        setUpDataConnection();
-        SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
-        ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
-        doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
-        doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
-        doReturn(false).when(mApnContext).isConnectable();
-        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        doReturn(DctConstants.State.DISCONNECTING).when(mApnContext).getState();
-        replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
-        replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
-
-        sendInitializationEvents();
-
-        logd("Sending EVENT_ENABLE_APN");
-        // APN id 0 is APN_TYPE_DEFAULT
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
-                mDct.obtainMessage(12345));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        assertTrue(isHandoverPending(ApnSetting.TYPE_IMS));
-
-        // Verify no handover request was sent
-        verify(mDataConnection, never()).bringUp(any(ApnContext.class), anyInt(), anyInt(),
-                any(Message.class), anyInt(), anyInt(), anyInt(), anyBoolean());
-
-        doReturn(DctConstants.State.RETRYING).when(mApnContext).getState();
-        // Data now is disconnected
-        doReturn(true).when(mApnContext).isConnectable();
-        doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
-                new AsyncResult(Pair.create(mApnContext, 0), null, null)));
-
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        verify(mDataConnection).bringUp(any(ApnContext.class), anyInt(), anyInt(),
-                any(Message.class), anyInt(), eq(DcTracker.REQUEST_TYPE_HANDOVER), anyInt(),
-                anyBoolean());
-    }
-
-    @Test
-    public void testDataUnthrottled() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_UNTHROTTLED,
-                new AsyncResult(null, FAKE_APN3, null)));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        verify(mDataThrottler).setRetryTime(
-                eq(ApnSetting.TYPE_IMS),
-                eq(RetryManager.NO_SUGGESTED_RETRY_DELAY),
-                eq(DcTracker.REQUEST_TYPE_NORMAL));
-    }
-
-    @Test
-    public void testDataUnthrottledAfterAPNChanged() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_CHANGED, null));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
-        // Verify unthrottling
-        verify(mDataThrottler, times(2)).reset();
-    }
-
-    @Test
-    public void testDataUnthrottledOnSimStateChanged() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-        sendSimStateUpdated("testDataUnthrottledOnSimStateChanged");
-
-        // Verify unthrottling
-        verify(mDataThrottler, times(2)).reset();
-    }
-
-    @Test
-    public void testHandlingSecondHandoverRequest() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        setUpDataConnection();
-        SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
-        ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
-        doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
-        doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
-        doReturn(false).when(mApnContext).isConnectable();
-        doReturn(DctConstants.State.CONNECTING).when(mApnContext).getState();
-        replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
-        replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
-
-        sendInitializationEvents();
-
-        logd("Sending EVENT_ENABLE_APN");
-        // APN id 0 is APN_TYPE_DEFAULT
-        Message msg = mDct.obtainMessage(12345);
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, msg);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        Map<Integer, List<Message>> msgs = getHandoverCompletionMessages();
-        // Make sure the messages was queued properly instead of fired right away.
-        assertTrue(msgs.get(ApnSetting.TYPE_IMS).contains(msg));
-    }
-
-    @Test
-    public void testDataThrottledNotAllowData() throws Exception {
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
-        doReturn(SystemClock.elapsedRealtime() + 100000).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_IMS);
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        sendInitializationEvents();
-
-        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-        boolean allowed = mDct.isDataAllowed(mApnContext, DcTracker.REQUEST_TYPE_NORMAL,
-                dataConnectionReasons);
-        assertFalse(dataConnectionReasons.toString(), allowed);
-        assertTrue(dataConnectionReasons.contains(DataDisallowedReasonType.DATA_THROTTLED));
-
-        // Makre sure no data setup request
-        verify(mSimulatedCommandsVerifier, never()).setupDataCall(
-                anyInt(), any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(),
-                anyInt(), any(), any(), anyBoolean(), any(Message.class));
-    }
-
-    @Test
-    public void testNotifyDataDisconnected() {
-        // Verify notify data disconnected on DCT constructor, initialized in setUp()
-        ArgumentCaptor<PreciseDataConnectionState> captor =
-                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
-        verify(mPhone, times(13)).notifyDataConnection(captor.capture());
-        for (PreciseDataConnectionState state : captor.getAllValues()) {
-            assertEquals(TelephonyManager.DATA_DISCONNECTED, state.getState());
-        }
-    }
-
-    /**
-     * There is a corresponding test {@link DataConnectionTest#testDataServiceTempUnavailable()} to
-     * test DataConnection behavior.
-     */
-    @Test
-    public void testDataServiceTempUnavailable() {
-        Handler handler = Mockito.mock(Handler.class);
-        Message handoverCompleteMessage = Message.obtain(handler);
-        addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_SETUP_COMPLETE,
-                DcTracker.REQUEST_TYPE_HANDOVER, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
-                new AsyncResult(Pair.create(mApnContext, 0),
-                        DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE, new Exception())));
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        // Ensure handover is not completed yet
-        verify(handler, never()).sendMessageDelayed(any(), anyLong());
-    }
-
-    @Test
-    public void testNormalRequestDoesNotFailHandoverRequest() {
-        Handler handler = Mockito.mock(Handler.class);
-        Message handoverCompleteMessage = Message.obtain(handler);
-        addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        // Ensure handover is not completed yet
-        verify(handler, never()).sendMessageDelayed(any(), anyLong());
-    }
-
-    @Test
-    public void testPreferenceChangedFallback() {
-        Handler handler = Mockito.mock(Handler.class);
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
-                .getPreferredTransport(anyInt());
-        Message handoverCompleteMessage = Message.obtain(handler);
-        addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
-        initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
-        mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
-                handoverCompleteMessage);
-        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-        Bundle bundle = handoverCompleteMessage.getData();
-        assertTrue(bundle.getBoolean("extra_handover_failure_fallback"));
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
deleted file mode 100644
index 4acfefb..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ /dev/null
@@ -1,996 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.dataconnection;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-
-import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.telephony.CarrierConfigManager;
-import android.telephony.data.ApnSetting;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.uicc.UiccController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-
-/**
- * APN retry manager tests
- */
-public class RetryManagerTest extends TelephonyTest {
-
-    // This is the real APN data for the Japanese carrier NTT Docomo.
-    private final ApnSetting mApn1 = new ApnSetting.Builder()
-            .setId(2163)
-            .setOperatorNumeric("44010")
-            .setEntryName("sp-mode")
-            .setApnName("spmode.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private final ApnSetting mApn2 = new ApnSetting.Builder()
-            .setId(2164)
-            .setOperatorNumeric("44010")
-            .setEntryName("mopera U")
-            .setApnName("mopera.net")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private final ApnSetting mApn3 = new ApnSetting.Builder()
-            .setId(2165)
-            .setOperatorNumeric("44010")
-            .setEntryName("b-mobile for Nexus")
-            .setApnName("bmobile.ne.jp")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
-            .setProtocol(ApnSetting.PROTOCOL_IP)
-            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
-            .setCarrierEnabled(true)
-            .build();
-
-    private PersistableBundle mBundle;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp(getClass().getSimpleName());
-        mBundle = mContextFixture.getCarrierConfigBundle();
-        doReturn(false).when(mPhone).isUsingNewDataStack();
-        replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
-        replaceInstance(UiccController.class, "mInstance", null, mUiccController);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mBundle = null;
-        super.tearDown();
-    }
-
-    /**
-     * Test the behavior of a retry manager with no waiting APNs set.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerEmpty() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:2000"});
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the basic retry scenario where only one APN and no retry configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerOneApnNoRetry() throws Exception {
-
-        mBundle.putStringArray(
-                CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the basic retry scenario where only one APN with two retries configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerOneApnTwoRetries() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"supl:2000,3000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_SUPL);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(3000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        // No matter how many times we call getNextApnSetting, it should always return the next APN
-        // with NO_RETRY because we've already reached the maximum retry count.
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the basic retry scenario where two waiting APNs with one retry configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerTwoApnsOneRetry() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"others:2000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the basic retry scenario where two waiting APNs with two retries configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerTwoApnsTwoRetries() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"dun:2000,5000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(5000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the basic retry scenario where two mms APNs with two retries configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerTwoMmsApnsTwoRetries() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"mms:      3000,6000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(3000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(6000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the permanent fail scenario with one APN configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerApnPermanentFailed() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"fota:1000,4000,7000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
-        waitingApns.add(apn);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_FOTA);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-
-        rm.markApnPermanentFailed(apn);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-    }
-
-    /**
-     * Test the permanent fail scenario with two APNs configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerApnPermanentFailedWithTwoApns() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"mms  :   1000,4000,7000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        rm.markApnPermanentFailed(myApn1);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(7000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the permanent fail scenario with three APNs configured.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerApnPermanentFailedWithThreeApns() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:2000:2000,3000:3000", "ims:1000,4000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-        waitingApns.add(myApn3);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_IMS);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        rm.markApnPermanentFailed(myApn2);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the permanent fail scenario with two APN all failed
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerApnPermanentFailedAll() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:1000,4000,7000,9000", "mms:1234,4123"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        rm.markApnPermanentFailed(myApn1);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(7000, delay);
-
-        rm.markApnPermanentFailed(myApn2);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-    }
-
-    /**
-     * Test the randomized delay scenario.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerDelayWithRandomization() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:default_randomization=1000,3000:2000,6000:3000,10000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertTrue(delay >= 3000 && delay < 5000);    // 3s + 2s rand
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertTrue(delay >= 6000 && delay < 9000);    // 6s + 3s rand
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertTrue(delay >= 10000 && delay < 11000);  // 10s + 1s default rand
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the retry forever scenario
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerRetryForever() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:max_retries=infinite,1000,2000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-    }
-
-    /**
-     * Test the explicit max retry scenario.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerExplicitMaxRetry() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"hipri:  max_retries=4,1000,2000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_HIPRI);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the fail fast scenario.
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerFailFast() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:1000,5000"});
-
-        mBundle.putLong(CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 2000);
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        waitingApns.add(ApnSetting.makeApnSetting(mApn1));
-        waitingApns.add(ApnSetting.makeApnSetting(mApn2));
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(true);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(true);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(true);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(true);
-        assertEquals(2000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(true);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the permanent fail scenario with two APN all failed and then reset
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerApnPermanentFailedAllAndThenReset() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"dun:1000,4000,7000,9000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        rm.markApnPermanentFailed(myApn1);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(7000, delay);
-
-        rm.markApnPermanentFailed(myApn2);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn == null);
-
-        // reset the retry manager
-
-        ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
-        waitingApns.clear();
-        waitingApns.add(myApn3);
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"dun:3000,8000"});
-
-        rm.setWaitingApns(waitingApns);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(3000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(8000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn3));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    private void assertRange(long low, long high, long value) {
-        if (value >= low && value <= high) return;
-        fail("Not in range[" + low + "," + high + "], value=" + value);
-    }
-
-    /**
-     * Test the scenario where modem suggests retry the current APN once
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerModemSuggestedRetryOnce() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"others:1000,4000,7000,9000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        // Network suggests retrying the current APN
-        doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_MMS);
-        delay = rm.getDelayForNextApn(false);
-        assertRange(2450, 2500, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_MMS);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        // Modem suggests retrying the current APN
-        //rm.setModemSuggestedDelay(30000);
-        doReturn(30000 + SystemClock.elapsedRealtime()).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_MMS);
-        delay = rm.getDelayForNextApn(false);
-        assertRange(29950, 30000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_MMS);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-    }
-
-    /**
-     * Test the scenario where modem suggests not retrying
-     */
-    @Test
-    @SmallTest
-    public void testRetryManagerModemSuggestedNoRetry() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"default:1000,4000,7000,9000"});
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        // Modem suggests retrying the current APN
-        doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_DEFAULT);
-        delay = rm.getDelayForNextApn(false);
-        assertRange(2450, 2500, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        doReturn(RetryManager.NO_RETRY).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_DEFAULT);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(RetryManager.NO_RETRY, delay);
-    }
-
-    /**
-     * Test the scenario that network suggests the same retry for too many times
-     */
-    @Test
-    @SmallTest
-    public void testRetryNetworkSuggestedRetryTooManyTimes() throws Exception {
-
-        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
-                new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
-
-        int maxRetryCount = 10;
-
-        mBundle.putInt(CarrierConfigManager
-                        .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
-                maxRetryCount);
-
-        ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
-        ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
-        ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
-        waitingApns.add(myApn1);
-        waitingApns.add(myApn2);
-
-        RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
-        rm.setWaitingApns(waitingApns);
-
-        ApnSetting nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        long delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(1000, delay);
-
-        for (int i = 0; i < maxRetryCount; i++) {
-            nextApn = rm.getNextApnSetting();
-            assertTrue(nextApn.equals(mApn1));
-            doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
-                    .getRetryTime(ApnSetting.TYPE_DEFAULT);
-            delay = rm.getDelayForNextApn(false);
-            assertRange(2450, 2500, delay);
-        }
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn1));
-        doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_DEFAULT);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(20000, delay);
-
-        nextApn = rm.getNextApnSetting();
-        assertTrue(nextApn.equals(mApn2));
-        doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
-                .getRetryTime(ApnSetting.TYPE_DEFAULT);
-        delay = rm.getDelayForNextApn(false);
-        assertEquals(4000, delay);
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
index 1273148..d6aad72 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
@@ -379,7 +379,36 @@
                 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
                 EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
 
-        assertFalse(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+        /* case 1: Check routing is not checked when comparing the same numbers. As routing will
+        be unknown for all numbers apart from DB. Check merge when both are not from DB then
+        routing value is merged from first number. */
+        assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+        assertEquals(num1, EmergencyNumber.mergeSameEmergencyNumbers(num1, num2));
+
+        num2 = new EmergencyNumber(
+                "911",
+                "jp",
+                "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        /* case 1: Check routing is not checked when comparing the same numbers. Check merge when
+        one of the number is from DB then routing value is merged from DB number. Along with
+        source value is masked with both*/
+        assertTrue(EmergencyNumber.areSameEmergencyNumbers(num1, num2));
+
+        num2 = new EmergencyNumber(
+                "911",
+                "jp",
+                "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        assertEquals(num2, EmergencyNumber.mergeSameEmergencyNumbers(num1, num2));
     }
 
     public void testSameEmergencyNumberDifferentSource() throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index 39ff133..6121127 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.emergency;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -26,6 +28,9 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
@@ -73,6 +78,7 @@
     private static final String EMERGENCY_NUMBER_DB_OTA_FILE = "eccdata_ota";
     private static final int CONFIG_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION = 99999;
     private static final String CONFIG_EMERGENCY_NUMBER_ADDRESS = "54321";
+    private static final String CONFIG_EMERGENCY_DUPLICATE_NUMBER = "4321";
     private static final String CONFIG_EMERGENCY_NUMBER_COUNTRY = "us";
     private static final String CONFIG_EMERGENCY_NUMBER_MNC = "";
     private static final String NON_3GPP_EMERGENCY_TEST_NUMBER = "9876543";
@@ -115,6 +121,8 @@
 
     private File mLocalDownloadDirectory;
     private ShortNumberInfo mShortNumberInfo;
+    private Context mMockContext;
+    private Resources mResources;
 
     @Before
     public void setUp() throws Exception {
@@ -124,14 +132,18 @@
         mSubControllerMock = mock(SubscriptionController.class);
         mPhone2 = mock(Phone.class);
         mContext = InstrumentationRegistry.getTargetContext();
+        mMockContext = mock(Context.class);
+        mResources = mock(Resources.class);
 
         doReturn(mContext).when(mPhone).getContext();
         doReturn(0).when(mPhone).getPhoneId();
         doReturn(SUB_ID_PHONE_1).when(mPhone).getSubId();
+        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion(HAL_SERVICE_VOICE);
 
         doReturn(mContext).when(mPhone2).getContext();
         doReturn(1).when(mPhone2).getPhoneId();
         doReturn(SUB_ID_PHONE_2).when(mPhone2).getSubId();
+        doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion(HAL_SERVICE_VOICE);
 
         initializeEmergencyNumberListTestSamples();
         mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
@@ -142,6 +154,9 @@
         // Copy an OTA file to the test directory to similate the OTA mechanism
         simulateOtaEmergencyNumberDb(mPhone);
 
+        AssetManager am = new AssetManager.Builder().build();
+        doReturn(am).when(mMockContext).getAssets();
+
         processAllMessages();
         logd("EmergencyNumberTrackerTest -Setup!");
     }
@@ -182,6 +197,14 @@
             EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
             EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
         mEmergencyNumberListTestSample.add(emergencyNumberForTest);
+
+        emergencyNumberForTest = new EmergencyNumber(
+                CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        mEmergencyNumberListTestSample.add(emergencyNumberForTest);
     }
 
     private void sendEmergencyNumberListFromRadio() {
@@ -268,6 +291,11 @@
         }
     }
 
+    private boolean hasDbEmergencyNumbers(List<EmergencyNumber> subList,
+            List<EmergencyNumber> list) {
+        return list.containsAll(subList);
+    }
+
     private boolean hasDbEmergencyNumber(EmergencyNumber number, List<EmergencyNumber> list) {
         return list.contains(number);
     }
@@ -368,8 +396,8 @@
     @Test
     public void testIsEmergencyNumber_FallbackToShortNumberXml_NoSims() throws Exception {
         // Set up the Hal version as 1.4
-        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
-        doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion();
+        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion(HAL_SERVICE_VOICE);
+        doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion(HAL_SERVICE_VOICE);
 
         setDsdsPhones();
         replaceInstance(SubscriptionController.class, "sInstance", null, mSubControllerMock);
@@ -388,7 +416,7 @@
         processAllMessages();
 
         replaceInstance(ShortNumberInfo.class, "INSTANCE", null, mShortNumberInfo);
-        mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, true);
+        mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER);
 
         //verify that we fall back to shortnumber xml when there are no SIMs
         verify(mShortNumberInfo).isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, "JP");
@@ -406,8 +434,8 @@
 
     private void testIsEmergencyNumber_NoFallbackToShortNumberXml(int numSims) throws Exception {
         // Set up the Hal version as 1.4
-        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
-        doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion();
+        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion(HAL_SERVICE_VOICE);
+        doReturn(new HalVersion(1, 4)).when(mPhone2).getHalVersion(HAL_SERVICE_VOICE);
 
         assertTrue((numSims > 0 && numSims < 3));
         setDsdsPhones();
@@ -436,7 +464,7 @@
         processAllMessages();
 
         replaceInstance(ShortNumberInfo.class, "INSTANCE", null, mShortNumberInfo);
-        mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER, true);
+        mEmergencyNumberTrackerMock.isEmergencyNumber(NON_3GPP_EMERGENCY_TEST_NUMBER);
 
         //verify we do not use ShortNumber xml
         verify(mShortNumberInfo, never()).isEmergencyNumber(anyString(), anyString());
@@ -480,14 +508,21 @@
      */
     @Test
     public void testUsingEmergencyNumberDatabaseWheneverHal_1_3() {
-        doReturn(new HalVersion(1, 3)).when(mPhone).getHalVersion();
+        doReturn(new HalVersion(1, 3)).when(mPhone).getHalVersion(HAL_SERVICE_VOICE);
+        doReturn(mMockContext).when(mPhone).getContext();
+        doReturn(mResources).when(mMockContext).getResources();
+        doReturn(true).when(mResources).getBoolean(
+                com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
 
-        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
-        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+        EmergencyNumberTracker emergencyNumberTracker = new EmergencyNumberTracker(
+                mPhone, mSimulatedCommands);
+
+        sendEmergencyNumberPrefix(emergencyNumberTracker);
+        emergencyNumberTracker.updateEmergencyCountryIsoAllPhones("us");
         processAllMessages();
 
         boolean hasDatabaseNumber = false;
-        for (EmergencyNumber number : mEmergencyNumberTrackerMock.getEmergencyNumberList()) {
+        for (EmergencyNumber number : emergencyNumberTracker.getEmergencyNumberList()) {
             if (number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) {
                 hasDatabaseNumber = true;
                 break;
@@ -501,13 +536,179 @@
      */
     @Test
     public void testUsingEmergencyNumberDatabaseWheneverHal_1_4() {
-        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+        doReturn(mMockContext).when(mPhone).getContext();
+        doReturn(mContext.getAssets()).when(mMockContext).getAssets();
+        doReturn(mResources).when(mMockContext).getResources();
+        doReturn(true).when(mResources).getBoolean(
+                com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
 
-        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
-        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+        EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
+                mPhone, mSimulatedCommands);
+        emergencyNumberTrackerMock.sendMessage(
+                emergencyNumberTrackerMock.obtainMessage(
+                        1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
+                        new AsyncResult(null, mEmergencyNumberListTestSample, null)));
+        sendEmergencyNumberPrefix(emergencyNumberTrackerMock);
+        emergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
         processAllMessages();
+        /* case 1: check DB number exist or not */
         assertTrue(hasDbEmergencyNumber(CONFIG_EMERGENCY_NUMBER,
-                mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        /* case 2: since ignore_emergency_routing_from_db is true. check for all DB numbers with
+        routing value as unknown by ignoring DB value */
+        List<EmergencyNumber> completeEmergencyNumberList = new ArrayList<>();
+        EmergencyNumber emergencyNumber = new EmergencyNumber(
+                "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        completeEmergencyNumberList.add(emergencyNumber);
+
+        emergencyNumber = new EmergencyNumber(
+                "54321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        completeEmergencyNumberList.add(emergencyNumber);
+
+        emergencyNumber = new EmergencyNumber(
+                "654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        completeEmergencyNumberList.add(emergencyNumber);
+
+        emergencyNumber = new EmergencyNumber(
+                "7654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        completeEmergencyNumberList.add(emergencyNumber);
+
+        assertTrue(hasDbEmergencyNumbers(completeEmergencyNumberList,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        /* case 3: check the routing type of merged duplicate numbers
+            between DB number and radio list. */
+        EmergencyNumber duplicateEmergencyNumber = new EmergencyNumber(
+                CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        assertTrue(hasDbEmergencyNumber(duplicateEmergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+    }
+
+    @Test
+    public void testUsingEmergencyNumberDatabaseWithRouting() {
+        doReturn(mMockContext).when(mPhone).getContext();
+        doReturn(mContext.getAssets()).when(mMockContext).getAssets();
+        doReturn(mResources).when(mMockContext).getResources();
+        doReturn("05").when(mCellIdentity).getMncString();
+        doReturn(false).when(mResources).getBoolean(
+                com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
+
+        EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
+                mPhone, mSimulatedCommands);
+        emergencyNumberTrackerMock.sendMessage(
+                emergencyNumberTrackerMock.obtainMessage(
+                        1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
+                        new AsyncResult(null, mEmergencyNumberListTestSample, null)));
+        sendEmergencyNumberPrefix(emergencyNumberTrackerMock);
+        emergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+        processAllMessages();
+
+        // case 1: check DB number with normal routing true and for mnc 05
+        EmergencyNumber emergencyNumber = new EmergencyNumber(
+                CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "05", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        // case 2: check DB number with normal routing true in multiple mnc 05, 45, 47
+        emergencyNumber = new EmergencyNumber(
+                "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "05", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        doReturn("47").when(mCellIdentity).getMncString();
+        emergencyNumber = new EmergencyNumber(
+                "888", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "47", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                        CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                            EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        emergencyNumber = new EmergencyNumber(
+                CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        /* case 3: check DB number with normal routing false and for mnc 05,
+            but current cell identity is 04 */
+        doReturn("04").when(mCellIdentity).getMncString();
+        emergencyNumber = new EmergencyNumber(
+                CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        // case 4: check DB number with normal routing false
+        emergencyNumber = new EmergencyNumber(
+                "654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        // case 5: check DB number with normal routing true & empty mnc
+        emergencyNumber = new EmergencyNumber(
+                "7654321", CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                    "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                            CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                                            EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
+
+        /* case 6: check DB number with normal routing true & empty mnc. But same number exist
+            in radio list. In merge DB routing should be used */
+        emergencyNumber = new EmergencyNumber(
+                CONFIG_EMERGENCY_DUPLICATE_NUMBER, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+                "", CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+                CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        assertTrue(hasDbEmergencyNumber(emergencyNumber,
+                emergencyNumberTrackerMock.getEmergencyNumberList()));
     }
 
     /**
@@ -516,7 +717,7 @@
     @Test
     public void testOtaEmergencyNumberDatabase() {
         // Set up the Hal version as 1.4 to apply emergency number database
-        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+        doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion(HAL_SERVICE_VOICE);
 
         sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
         mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 491c690..f960aff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -1258,10 +1258,17 @@
 
     private void setHasCarrierPrivilegesOnActiveSubscription(boolean hasPrivileges)
             throws Exception {
-        SubscriptionInfo subInfo = new SubscriptionInfo(
-                0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
-                hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "", CARD_ID,
-                false, null, false, 0, 0, 0, null, null, true, 0);
+        SubscriptionInfo.Builder builder = new SubscriptionInfo.Builder()
+                .setSimSlotIndex(0)
+                .setPortIndex(mTelephonyManager.DEFAULT_PORT_INDEX)
+                .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER_ID)
+                .setEmbedded(true);
+        if (hasPrivileges) {
+            builder.setNativeAccessRules(new UiccAccessRule[] { ACCESS_RULE });
+        }
+        builder.setCardId(CARD_ID);
+        SubscriptionInfo subInfo = builder.build();
+
         when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
                 hasPrivileges);
         when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(
@@ -1287,14 +1294,18 @@
         cardInfos.add(cardInfo2);
         when(mTelephonyManager.getUiccCardsInfo()).thenReturn(cardInfos);
 
-        SubscriptionInfo subInfo1 = new SubscriptionInfo(
-                0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
-                hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "", CARD_ID,
-                false, null, false, 0, 0, 0, null, null, true, 0);
-        SubscriptionInfo subInfo2 = new SubscriptionInfo(
-                0, "", 0, "", "", 0, 0, "", 0, null, "", "", "", true /* isEmbedded */,
-                hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null, "",
-                1 /* cardId */, false, null, false, 0, 0, 0, null, null, true, 0);
+        SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+                .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+                .setEmbedded(true)
+                .setCardId(CARD_ID)
+                .setPortIndex(mTelephonyManager.DEFAULT_PORT_INDEX)
+                .build();
+        SubscriptionInfo subInfo2 = new SubscriptionInfo.Builder()
+                .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+                .setEmbedded(true)
+                .setCardId(2)
+                .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+                .build();
         when(mSubscriptionManager.canManageSubscription(subInfo1, PACKAGE_NAME)).thenReturn(
                 hasPrivileges);
         when(mSubscriptionManager.canManageSubscription(subInfo2, PACKAGE_NAME)).thenReturn(
@@ -1304,10 +1315,13 @@
     }
 
     private void prepareOperationSubscription(boolean hasPrivileges) throws Exception {
-        SubscriptionInfo subInfo = new SubscriptionInfo(
-                SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, "0", "0", "",
-                true /* isEmbedded */, hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null,
-                null);
+        SubscriptionInfo subInfo = new SubscriptionInfo.Builder()
+                .setId(SUBSCRIPTION_ID)
+                .setIccId(ICC_ID)
+                .setNativeAccessRules(hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null)
+                .setEmbedded(true)
+                .setCardId(CARD_ID)
+                .build();
         when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
                 hasPrivileges);
         when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
deleted file mode 100644
index 2e9b88c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
+++ /dev/null
@@ -1,1935 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF GSMTestHandler.ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.ServiceState;
-import android.test.AndroidTestCase;
-import android.test.PerformanceTestCase;
-
-import android.telephony.DisconnectCause;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.MmiCode;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.gsm.CallFailCause;
-import com.android.internal.telephony.gsm.GSMPhone;
-import com.android.internal.telephony.gsm.GSMTestHandler;
-import com.android.internal.telephony.gsm.GsmMmiCode;
-import com.android.internal.telephony.gsm.SuppServiceNotification;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-
-import java.util.List;
-
-
-public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase {
-    private SimulatedRadioControl mRadioControl;
-    private GSMPhone mGSMPhone;
-    private GSMTestHandler mGSMTestHandler;
-    private Handler mHandler;
-
-    private static final int EVENT_PHONE_STATE_CHANGED = 1;
-    private static final int EVENT_DISCONNECT = 2;
-    private static final int EVENT_RINGING = 3;
-    private static final int EVENT_CHANNEL_OPENED = 4;
-    private static final int EVENT_POST_DIAL = 5;
-    private static final int EVENT_DONE = 6;
-    private static final int EVENT_SSN = 7;
-    private static final int EVENT_MMI_INITIATE = 8;
-    private static final int EVENT_MMI_COMPLETE = 9;
-    private static final int EVENT_IN_SERVICE = 10;
-    private static final int SUPP_SERVICE_FAILED = 11;
-    private static final int SERVICE_STATE_CHANGED = 12;
-    private static final int EVENT_OEM_RIL_MESSAGE = 13;
-    public static final int ANY_MESSAGE = -1;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mGSMTestHandler = new GSMTestHandler(mContext);
-
-        mGSMTestHandler.start();
-        synchronized (mGSMTestHandler) {
-            do {
-                mGSMTestHandler.wait();
-            } while (mGSMTestHandler.getGSMPhone() == null);
-        }
-
-        mGSMPhone = mGSMTestHandler.getGSMPhone();
-        mRadioControl = mGSMTestHandler.getSimulatedCommands();
-
-        mHandler = mGSMTestHandler.getHandler();
-        mGSMPhone.registerForPreciseCallStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
-        mGSMPhone.registerForNewRingingConnection(mHandler, EVENT_RINGING, null);
-        mGSMPhone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
-
-        mGSMPhone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL, null);
-
-        mGSMPhone.registerForSuppServiceNotification(mHandler, EVENT_SSN, null);
-        mGSMPhone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
-        mGSMPhone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
-        mGSMPhone.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED, null);
-
-        mGSMPhone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
-
-        // wait until we get phone in both voice and data service
-        Message msg;
-        ServiceState state;
-
-        do {
-            msg = mGSMTestHandler.waitForMessage(SERVICE_STATE_CHANGED);
-            assertNotNull("Message Time Out", msg);
-            state = (ServiceState) ((AsyncResult) msg.obj).result;
-        } while (state.getState() != ServiceState.STATE_IN_SERVICE);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mRadioControl.shutdown();
-
-        mGSMPhone.unregisterForPreciseCallStateChanged(mHandler);
-        mGSMPhone.unregisterForNewRingingConnection(mHandler);
-        mGSMPhone.unregisterForDisconnect(mHandler);
-        mGSMPhone.setOnPostDialCharacter(mHandler, 0, null);
-        mGSMPhone.unregisterForSuppServiceNotification(mHandler);
-        mGSMPhone.unregisterForMmiInitiate(mHandler);
-        mGSMPhone.unregisterForMmiComplete(mHandler);
-
-        mGSMPhone = null;
-        mRadioControl = null;
-        mHandler = null;
-        mGSMTestHandler.cleanup();
-
-        super.tearDown();
-    }
-
-    // These test can only be run once.
-    public int startPerformance(Intermediates intermediates) {
-        return 1;
-    }
-
-    public boolean isPerformanceOnly() {
-        return false;
-    }
-
-
-    //This test is causing the emulator screen to turn off. I don't understand
-    //why, but I'm removing it until we can figure it out.
-    public void brokenTestGeneral() throws Exception {
-        Connection cn;
-        Message msg;
-        AsyncResult ar;
-
-        // IDLE state
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-        assertFalse(mGSMPhone.canConference());
-
-        // One DIALING connection
-
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        mGSMPhone.dial("+13125551212");
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_PHONE_STATE_CHANGED);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertEquals(Call.State.DIALING, mGSMPhone.getForegroundCall().getState());
-        assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        /*do {
-            mGSMTestHandler.waitForMessage(ANY_MESSAGE);
-        } while (mGSMPhone.getForegroundCall().getConnections().size() == 0);*/
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DIALING,
-                mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        cn = mGSMPhone.getForegroundCall().getConnections().get(0);
-        assertTrue(!cn.isIncoming());
-        assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // One ALERTING connection
-
-        mRadioControl.progressConnectingCallState();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        }
-        while (mGSMPhone.getForegroundCall().getState() != Call.State.ALERTING);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ALERTING, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        cn = mGSMPhone.getForegroundCall().getConnections().get(0);
-        assertTrue(!cn.isIncoming());
-        assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-        assertFalse(mGSMPhone.canConference());
-
-        // One ACTIVE connection
-
-        mRadioControl.progressConnectingCallState();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
-        cn = mGSMPhone.getForegroundCall().getConnections().get(0);
-        assertTrue(!cn.isIncoming());
-        assertEquals(Connection.PostDialState.COMPLETE, cn.getPostDialState());
-        assertFalse(mGSMPhone.canConference());
-
-        // One disconnected connection
-        mGSMPhone.getForegroundCall().hangup();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
-        assertFalse(mGSMPhone.canConference());
-
-        cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        // Back to idle state
-
-        mGSMPhone.clearDisconnected();
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // cn left over from before phone.clearDisconnected();
-
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        // One ringing (INCOMING) call
-
-        mRadioControl.triggerRing("18005551212");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        ar = (AsyncResult) msg.obj;
-        cn = (Connection) ar.result;
-        assertTrue(cn.isRinging());
-        assertEquals(mGSMPhone.getRingingCall(), cn.getCall());
-
-        assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.INCOMING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        cn = mGSMPhone.getRingingCall().getConnections().get(0);
-        assertTrue(cn.isIncoming());
-        assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // One mobile terminated active call
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getConnections().size() == 1);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE,
-                mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
-        cn = mGSMPhone.getForegroundCall().getConnections().get(0);
-        assertTrue(cn.isIncoming());
-        assertEquals(Connection.PostDialState.NOT_STARTED, cn.getPostDialState());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // One disconnected (local hangup) call
-
-        try {
-            Connection conn;
-            conn = mGSMPhone.getForegroundCall().getConnections().get(0);
-            conn.hangup();
-        } catch (CallStateException ex) {
-            ex.printStackTrace();
-            fail("unexpected ex");
-        }
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED,
-                mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestConnectTime() > 0);
-
-        cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // Back to idle state
-
-        mGSMPhone.clearDisconnected();
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // cn left over from before phone.clearDisconnected();
-
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        // One ringing call
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getConnections().isEmpty());
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.INCOMING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // One rejected call
-        mGSMPhone.rejectCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(1, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getRingingCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getRingingCall().getEarliestConnectTime());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        cn = mGSMPhone.getRingingCall().getEarliestConnection();
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-
-        assertFalse(mGSMPhone.canConference());
-
-        // Back to idle state
-
-        mGSMPhone.clearDisconnected();
-
-        assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestCreateTime());
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        assertFalse(mGSMPhone.canConference());
-        assertEquals(Call.State.DISCONNECTED, cn.getState());
-
-        // One ringing call
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getConnections().isEmpty());
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
-        // Ringing call disconnects
-
-        mRadioControl.triggerHangupForeground();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
-        assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-
-        // One Ringing Call
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-
-        cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
-        // One answered call
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // one holding call
-        mGSMPhone.switchHoldingAndActive();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // one active call
-        mGSMPhone.switchHoldingAndActive();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        }
-        while (mGSMPhone.getBackgroundCall().getState() == Call.State.HOLDING);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // One disconnected call in the foreground slot
-
-        mRadioControl.triggerHangupAll();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
-        // Test missed calls
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-        mGSMPhone.rejectCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (msg.what != EVENT_DISCONNECT);
-
-        ar = (AsyncResult) msg.obj;
-        cn = (Connection) ar.result;
-
-        assertEquals(DisconnectCause.INCOMING_MISSED, cn.getDisconnectCause());
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
-
-        // Test incoming not missed calls
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-        cn = mGSMPhone.getRingingCall().getEarliestConnection();
-
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, cn.getDisconnectCause());
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-
-        try {
-            mGSMPhone.getForegroundCall().hangup();
-        } catch (CallStateException ex) {
-            ex.printStackTrace();
-            fail("unexpected ex");
-        }
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState()
-                != Call.State.DISCONNECTED);
-
-        assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
-        //
-        // Test held and hangup held calls
-        //
-
-        // One ALERTING call
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        assertTrue(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        mRadioControl.progressConnectingCallState();
-        mRadioControl.progressConnectingCallState();
-
-        // One ACTIVE call
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        // One ACTIVE call, one ringing call
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        // One HOLDING call, one ACTIVE call
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertTrue(mGSMPhone.canConference());
-
-        // Conference the two
-        mGSMPhone.conference();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertTrue(mGSMPhone.getForegroundCall().isMultiparty());
-        assertFalse(mGSMPhone.canConference());
-
-        // Hold the multiparty call
-        mGSMPhone.switchHoldingAndActive();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        }
-        while (mGSMPhone.getBackgroundCall().getState() != Call.State.HOLDING);
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertTrue(mGSMPhone.getBackgroundCall().isMultiparty());
-        assertFalse(mGSMPhone.canConference());
-
-        // Multiparty call on hold, call waiting added
-
-        mRadioControl.triggerRing("18005558355");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.RINGING);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertTrue(mGSMPhone.getBackgroundCall().isMultiparty());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertFalse(mGSMPhone.canConference());
-
-        // Hangup conference call, ringing call still around
-        mGSMPhone.getBackgroundCall().hangup();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.DISCONNECTED);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getBackgroundCall().getState());
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-
-        // Reject waiting call
-        mGSMPhone.rejectCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.IDLE);
-
-        assertFalse(mGSMPhone.getForegroundCall().isDialingOrAlerting());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-    }
-
-    public void testOutgoingCallFailImmediately() throws Exception {
-        Message msg;
-
-        // Test outgoing call fail-immediately edge case
-        // This happens when a call terminated before ever appearing in a
-        // call list
-        // This should land the immediately-failing call in the
-        // ForegroundCall list as an IDLE call
-        mRadioControl.setNextDialFailImmediately(true);
-
-        Connection cn = mGSMPhone.dial("+13125551212");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-    }
-
-    public void testHangupOnOutgoing() throws Exception {
-        Connection cn;
-        Message msg;
-
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        // Test 1: local hangup in "DIALING" state
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        }
-        while (mGSMPhone.getForegroundCall().getState() != Call.State.DIALING);
-
-        cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
-        mGSMPhone.getForegroundCall().hangup();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
-        // Test 2: local hangup in "ALERTING" state
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        mRadioControl.progressConnectingCallState();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        }
-        while (mGSMPhone.getForegroundCall().getState() != Call.State.ALERTING);
-
-        cn = mGSMPhone.getForegroundCall().getEarliestConnection();
-
-        mGSMPhone.getForegroundCall().hangup();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(DisconnectCause.LOCAL, cn.getDisconnectCause());
-
-        // Test 3: local immediate hangup before GSM index is
-        // assigned (CallTracker.hangupPendingMO case)
-
-        mRadioControl.pauseResponses();
-
-        cn = mGSMPhone.dial("+13125551212");
-
-        cn.hangup();
-
-        mRadioControl.resumeResponses();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-
-        assertEquals(DisconnectCause.LOCAL,
-                mGSMPhone.getForegroundCall().getEarliestConnection().getDisconnectCause());
-    }
-
-    public void testHangupOnChannelClose() throws Exception {
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getConnections().isEmpty());
-
-        mRadioControl.shutdown();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-            mGSMPhone.clearDisconnected();
-        } while (!mGSMPhone.getForegroundCall().getConnections().isEmpty());
-    }
-
-    public void testIncallMmiCallDeflection() throws Exception {
-        Message msg;
-
-        // establish an active call
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // establish a ringing (WAITING) call
-
-        mRadioControl.triggerRing("18005551212");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering 0 followed by SEND: release all held calls
-        // or sets UDUB for a waiting call.
-        mGSMPhone.handleInCallMmiCommands("0");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // change the active call to holding call
-        mGSMPhone.switchHoldingAndActive();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering 0 followed by SEND: release all held calls
-        // or sets UDUB for a waiting call.
-        mGSMPhone.handleInCallMmiCommands("0");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() == Call.State.HOLDING);
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getBackgroundCall().getState());
-    }
-
-    public void testIncallMmiCallWaiting() throws Exception {
-        Message msg;
-
-        // establish an active call
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // establish a ringing (WAITING) call
-
-        mRadioControl.triggerRing("18005551212");
-
-        do {
-            msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
-            assertNotNull("Message Time Out", msg);
-        } while (msg.what != EVENT_RINGING);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering 1 followed by SEND: release all active calls
-        // (if any exist) and accepts the other (held or waiting) call.
-
-        mGSMPhone.handleInCallMmiCommands("1");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
-        // change the active call to holding call
-        mGSMPhone.switchHoldingAndActive();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering 1 followed by SEND: release all active calls
-        // (if any exist) and accepts the other (held or waiting) call.
-        mGSMPhone.handleInCallMmiCommands("1");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
-        // at this point, the active call with number==18005551212 should
-        // have the gsm index of 2
-
-        mRadioControl.triggerRing("16505550100");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering "12" followed by SEND: release the call with
-        // gsm index equals to 2.
-        mGSMPhone.handleInCallMmiCommands("12");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getState() != PhoneConstants.State.OFFHOOK);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // at this point, the call with number==16505550100 should
-        // have the gsm index of 1
-        mGSMPhone.dial("+13125551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE ||
-                mGSMPhone.getBackgroundCall().getState() != Call.State.HOLDING);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // at this point, the active call with number==13125551212 should
-        // have the gsm index of 2
-
-        // Simulate entering "11" followed by SEND: release the call with
-        // gsm index equals to 1. This should not be allowed, and a
-        // Supplementary Service notification must be received.
-        mGSMPhone.handleInCallMmiCommands("11");
-
-        msg = mGSMTestHandler.waitForMessage(SUPP_SERVICE_FAILED);
-        assertNotNull("Message Time Out", msg);
-        assertFalse("IncallMmiCallWaiting: command should not work on holding call", msg == null);
-
-        // Simulate entering "12" followed by SEND: release the call with
-        // gsm index equals to 2.
-        mGSMPhone.handleInCallMmiCommands("12");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // Simulate entering 1 followed by SEND: release all active calls
-        // (if any exist) and accepts the other (held or waiting) call.
-        mGSMPhone.handleInCallMmiCommands("1");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("16505550100",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-
-        // Simulate entering "11" followed by SEND: release the call with
-        // gsm index equals to 1.
-        mGSMPhone.handleInCallMmiCommands("11");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-    }
-
-    public void testIncallMmiCallHold() throws Exception {
-        Message msg;
-
-        // establish an active call
-        mGSMPhone.dial("13125551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // establish a ringing (WAITING) call
-
-        mRadioControl.triggerRing("18005551212");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // simulate entering 2 followed by SEND: place all active calls
-        // (if any exist) on hold and accepts the other (held or waiting)
-        // call
-
-        mGSMPhone.handleInCallMmiCommands("2");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getState() == Call.State.WAITING);
-
-
-        assertFalse(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE,
-                mGSMPhone.getForegroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("13125551212",
-                mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
-        // swap the active and holding calls
-        mGSMPhone.handleInCallMmiCommands("2");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_PHONE_STATE_CHANGED);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("13125551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
-        // merge the calls
-        mGSMPhone.conference();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-        assertEquals(2, mGSMPhone.getForegroundCall().getConnections().size());
-
-        // at this point, we have an active conference call, with
-        // call(1) = 13125551212 and call(2) = 18005551212
-
-        // Simulate entering "23" followed by SEND: places all active call
-        // on hold except call 3. This should fail and a supplementary service
-        // failed notification should be received.
-
-        mGSMPhone.handleInCallMmiCommands("23");
-
-        msg = mGSMTestHandler.waitForMessage(SUPP_SERVICE_FAILED);
-        assertNotNull("Message Time Out", msg);
-        assertFalse("IncallMmiCallHold: separate should have failed!", msg == null);
-
-        // Simulate entering "21" followed by SEND: places all active call
-        // on hold except call 1.
-        mGSMPhone.handleInCallMmiCommands("21");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() == Call.State.IDLE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("13125551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-    }
-
-    public void testIncallMmiMultipartyServices() throws Exception {
-        // establish an active call
-        mGSMPhone.dial("13125551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        // dial another call
-        mGSMPhone.dial("18005551212");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        mGSMPhone.handleInCallMmiCommands("3");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-        assertEquals(PhoneConstants.State.OFFHOOK, mGSMPhone.getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals("13125551212",
-                mGSMPhone.getForegroundCall().getConnections().get(1).getAddress());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-    }
-
-    public void testCallIndex() throws Exception {
-        Message msg;
-
-        // establish the first call
-        mGSMPhone.dial("16505550100");
-
-        do {
-            mRadioControl.progressConnectingCallState();
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        String baseNumber = "1650555010";
-
-        for (int i = 1; i < 6; i++) {
-            String number = baseNumber + i;
-
-            mGSMPhone.dial(number);
-
-            do {
-                mRadioControl.progressConnectingCallState();
-                assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-            } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-            assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-            assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-            if (mGSMPhone.getBackgroundCall().getConnections().size() >= 5) {
-                break;
-            }
-
-            mGSMPhone.conference();
-
-            do {
-                assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-            } while (mGSMPhone.getBackgroundCall().getState() != Call.State.IDLE);
-
-            assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-            assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-        }
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("16505550105",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // create an incoming call, this call should have the call index
-        // of 7
-        mRadioControl.triggerRing("18005551212");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_RINGING);
-        assertNotNull("Message Time Out", msg);
-
-        assertEquals(PhoneConstants.State.RINGING, mGSMPhone.getState());
-        assertTrue(mGSMPhone.getRingingCall().isRinging());
-        assertEquals(Call.State.WAITING, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-
-        // hangup the background call and accept the ringing call
-        mGSMPhone.getBackgroundCall().hangup();
-        mGSMPhone.acceptCall();
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getRingingCall().getState() != Call.State.IDLE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals("18005551212",
-                mGSMPhone.getForegroundCall().getConnections().get(0).getAddress());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("16505550105",
-                mGSMPhone.getBackgroundCall().getConnections().get(0).getAddress());
-
-        mGSMPhone.handleInCallMmiCommands("17");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.HOLDING, mGSMPhone.getBackgroundCall().getState());
-        assertEquals("16505550105",
-                mGSMPhone.getBackgroundCall().getConnections().get(0).
-                        getAddress());
-
-        mGSMPhone.handleInCallMmiCommands("1");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() != Call.State.ACTIVE);
-
-        assertEquals(Call.State.ACTIVE, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        mGSMPhone.handleInCallMmiCommands("16");
-
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (mGSMPhone.getForegroundCall().getState() == Call.State.ACTIVE);
-
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-    }
-
-    public void testPostDialSequences() throws Exception {
-        Message msg;
-        AsyncResult ar;
-        Connection cn;
-
-        mGSMPhone.dial("+13125551212,1234;5N8xx");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(',', msg.arg1);
-        assertEquals("1234;5N8", cn.getRemainingPostDialString());
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('1', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('2', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('3', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('4', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(';', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(Connection.PostDialState.WAIT, cn.getPostDialState());
-        assertEquals(Connection.PostDialState.WAIT, ar.userObj);
-        cn.proceedAfterWaitChar();
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('5', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertEquals('N', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(Connection.PostDialState.WILD, cn.getPostDialState());
-        assertEquals(Connection.PostDialState.WILD, ar.userObj);
-        cn.proceedAfterWildChar(",6;7");
-
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(',', msg.arg1);
-        assertEquals("6;78", cn.getRemainingPostDialString());
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('6', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(';', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(Connection.PostDialState.WAIT, cn.getPostDialState());
-        assertEquals(Connection.PostDialState.WAIT, ar.userObj);
-        cn.proceedAfterWaitChar();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('7', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals('8', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        assertEquals(Connection.PostDialState.STARTED, ar.userObj);
-
-        // Bogus chars at end should be ignored
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(0, msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(Connection.PostDialState.COMPLETE,
-                cn.getPostDialState());
-        assertEquals(Connection.PostDialState.COMPLETE, ar.userObj);
-    }
-
-    public void testPostDialCancel() throws Exception {
-        Message msg;
-        AsyncResult ar;
-        Connection cn;
-
-        mGSMPhone.dial("+13125551212,N");
-        mRadioControl.progressConnectingToActive();
-
-        mRadioControl.progressConnectingToActive();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(',', msg.arg1);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_POST_DIAL);
-        assertEquals('N', msg.arg1);
-        ar = (AsyncResult) (msg.obj);
-        cn = (Connection) (ar.result);
-        assertEquals(Connection.PostDialState.WILD, cn.getPostDialState());
-        cn.cancelPostDial();
-
-        assertEquals(Connection.PostDialState.CANCELLED, cn.getPostDialState());
-    }
-
-    public void testOutgoingCallFail() throws Exception {
-        Message msg;
-        /*
-        * normal clearing
-        */
-
-        mRadioControl.setNextCallFailCause(CallFailCause.NORMAL_CLEARING);
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        Connection cn = mGSMPhone.dial("+13125551212");
-
-        mRadioControl.progressConnectingCallState();
-
-        // I'm just progressing the call state to
-        // ensure getCurrentCalls() gets processed...
-        // Normally these failure conditions would happen in DIALING
-        // not ALERTING
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (cn.getState() == Call.State.DIALING);
-
-
-        mRadioControl.triggerHangupAll();
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(DisconnectCause.NORMAL, cn.getDisconnectCause());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        /*
-        * busy
-        */
-
-        mRadioControl.setNextCallFailCause(CallFailCause.USER_BUSY);
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        cn = mGSMPhone.dial("+13125551212");
-
-        mRadioControl.progressConnectingCallState();
-
-        // I'm just progressing the call state to
-        // ensure getCurrentCalls() gets processed...
-        // Normally these failure conditions would happen in DIALING
-        // not ALERTING
-        do {
-            assertNotNull("Message Time Out", mGSMTestHandler.waitForMessage(ANY_MESSAGE));
-        } while (cn.getState() == Call.State.DIALING);
-
-
-        mRadioControl.triggerHangupAll();
-        msg = mGSMTestHandler.waitForMessage(EVENT_DISCONNECT);
-        assertNotNull("Message Time Out", msg);
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(DisconnectCause.BUSY, cn.getDisconnectCause());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED,
-                mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-
-        /*
-        * congestion
-        */
-
-        mRadioControl.setNextCallFailCause(CallFailCause.NO_CIRCUIT_AVAIL);
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        cn = mGSMPhone.dial("+13125551212");
-
-        mRadioControl.progressConnectingCallState();
-
-        // I'm just progressing the call state to
-        // ensure getCurrentCalls() gets processed...
-        // Normally these failure conditions would happen in DIALING
-        // not ALERTING
-        do {
-            msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
-            assertNotNull("Message Time Out", msg);
-        } while (cn.getState() == Call.State.DIALING);
-
-
-        mRadioControl.triggerHangupAll();
-
-        // Unlike the while loops above, this one waits
-        // for a "phone state changed" message back to "idle"
-        do {
-            msg = mGSMTestHandler.waitForMessage(ANY_MESSAGE);
-            assertNotNull("Message Time Out", msg);
-        } while (!(msg.what == EVENT_PHONE_STATE_CHANGED
-                && mGSMPhone.getState() == PhoneConstants.State.IDLE));
-
-        assertEquals(PhoneConstants.State.IDLE, mGSMPhone.getState());
-
-        assertEquals(DisconnectCause.CONGESTION, cn.getDisconnectCause());
-
-        assertEquals(0, mGSMPhone.getRingingCall().getConnections().size());
-        assertEquals(1, mGSMPhone.getForegroundCall().getConnections().size());
-        assertEquals(0, mGSMPhone.getBackgroundCall().getConnections().size());
-
-        assertEquals(Call.State.IDLE, mGSMPhone.getRingingCall().getState());
-        assertEquals(Call.State.DISCONNECTED, mGSMPhone.getForegroundCall().getState());
-        assertEquals(Call.State.IDLE, mGSMPhone.getBackgroundCall().getState());
-
-        assertTrue(mGSMPhone.getForegroundCall().getEarliestCreateTime() > 0);
-        assertEquals(0, mGSMPhone.getForegroundCall().getEarliestConnectTime());
-    }
-
-    public void testSSNotification() throws Exception {
-        // MO
-        runTest(0, SuppServiceNotification.MO_CODE_UNCONDITIONAL_CF_ACTIVE);
-        runTest(0, SuppServiceNotification.MO_CODE_CALL_IS_WAITING);
-        runTest(0, SuppServiceNotification.MO_CODE_CALL_DEFLECTED);
-
-        // MT
-        runTest(1, SuppServiceNotification.MT_CODE_FORWARDED_CALL);
-        runTest(1, SuppServiceNotification.MT_CODE_CALL_CONNECTED_ECT);
-        runTest(1, SuppServiceNotification.MT_CODE_ADDITIONAL_CALL_FORWARDED);
-    }
-
-    private void runTest(int type, int code) {
-        Message msg;
-
-        mRadioControl.triggerSsn(type, code);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_SSN);
-        assertNotNull("Message Time Out", msg);
-        AsyncResult ar = (AsyncResult) msg.obj;
-
-        assertNull(ar.exception);
-
-        SuppServiceNotification notification =
-                (SuppServiceNotification) ar.result;
-
-        assertEquals(type, notification.notificationType);
-        assertEquals(code, notification.code);
-    }
-
-    public void testUssd() throws Exception {
-        // Quick hack to work around a race condition in this test:
-        // We may initiate a USSD MMI before GSMPhone receives its initial
-        // GSMTestHandler.EVENT_RADIO_OFF_OR_NOT_AVAILABLE event.  When the phone sees this
-        // event, it will cancel the just issued USSD MMI, which we don't
-        // want.  So sleep a little first.
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException ex) {
-            // do nothing
-        }
-
-        verifyNormal();
-        verifyCancel();
-        varifyNetworkInitiated();
-    }
-
-    private void varifyNetworkInitiated() {
-        Message msg;
-        AsyncResult ar;
-        MmiCode mmi;
-
-        // Receive an incoming NOTIFY
-        mRadioControl.triggerIncomingUssd("0", "NOTIFY message");
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertFalse(mmi.isUssdRequest());
-
-        // Receive a REQUEST and send response
-        mRadioControl.triggerIncomingUssd("1", "REQUEST Message");
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertTrue(mmi.isUssdRequest());
-
-        mGSMPhone.sendUssdResponse("## TEST: TEST_GSMPhone responding...");
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        GsmMmiCode gsmMmi = (GsmMmiCode) mmi;
-        assertTrue(gsmMmi.isPendingUSSD());
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertNull(ar.exception);
-        assertFalse(mmi.isUssdRequest());
-
-        // Receive a REQUEST and cancel
-        mRadioControl.triggerIncomingUssd("1", "REQUEST Message");
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertTrue(mmi.isUssdRequest());
-
-        mmi.cancel();
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertNull(ar.exception);
-        assertEquals(MmiCode.State.CANCELLED, mmi.getState());
-
-        List mmiList = mGSMPhone.getPendingMmiCodes();
-        assertEquals(0, mmiList.size());
-    }
-
-    private void verifyNormal() throws CallStateException {
-        Message msg;
-        AsyncResult ar;
-        MmiCode mmi;
-
-        mGSMPhone.dial("#646#");
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
-        assertNotNull("Message Time Out", msg);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-        assertEquals(MmiCode.State.COMPLETE, mmi.getState());
-    }
-
-
-    private void verifyCancel() throws CallStateException {
-        /**
-         * This case makes an assumption that dial() will add the USSD
-         * to the "pending MMI codes" list before it returns.  This seems
-         * like reasonable semantics. It also assumes that the USSD
-         * request in question won't complete until we get back to the
-         * event loop, thus cancel() is safe.
-         */
-        Message msg;
-
-        mGSMPhone.dial("#646#");
-
-        List<? extends MmiCode> pendingMmis = mGSMPhone.getPendingMmiCodes();
-
-        assertEquals(1, pendingMmis.size());
-
-        MmiCode mmi = pendingMmis.get(0);
-        assertTrue(mmi.isCancelable());
-        mmi.cancel();
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
-        assertNotNull("Message Time Out", msg);
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-
-        AsyncResult ar = (AsyncResult) msg.obj;
-        mmi = (MmiCode) ar.result;
-
-        assertEquals(MmiCode.State.CANCELLED, mmi.getState());
-    }
-
-    public void testRilHooks() throws Exception {
-        //
-        // These test cases all assume the RIL OEM hooks
-        // just echo back their input
-        //
-
-        Message msg;
-        AsyncResult ar;
-
-        // null byte array
-
-        mGSMPhone.invokeOemRilRequestRaw(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertNull(ar.result);
-        assertNull(ar.exception);
-
-        // empty byte array
-
-        mGSMPhone.invokeOemRilRequestRaw(new byte[0], mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertEquals(0, ((byte[]) (ar.result)).length);
-        assertNull(ar.exception);
-
-        // byte array with data
-
-        mGSMPhone.invokeOemRilRequestRaw("Hello".getBytes("utf-8"),
-                mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertEquals("Hello", new String(((byte[]) (ar.result)), "utf-8"));
-        assertNull(ar.exception);
-
-        // null strings
-
-        mGSMPhone.invokeOemRilRequestStrings(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertNull(ar.result);
-        assertNull(ar.exception);
-
-        // empty byte array
-
-        mGSMPhone.invokeOemRilRequestStrings(new String[0],
-                mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertEquals(0, ((String[]) (ar.result)).length);
-        assertNull(ar.exception);
-
-        // Strings with data
-
-        String s[] = new String[1];
-
-        s[0] = "Hello";
-
-        mGSMPhone.invokeOemRilRequestStrings(s, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
-        assertNotNull("Message Time Out", msg);
-
-        ar = ((AsyncResult) msg.obj);
-
-        assertEquals("Hello", ((String[]) (ar.result))[0]);
-        assertEquals(1, ((String[]) (ar.result)).length);
-        assertNull(ar.exception);
-    }
-
-    public void testMmi() throws Exception {
-        mRadioControl.setAutoProgressConnectingCall(false);
-
-        // "valid" MMI sequences
-        runValidMmi("*#67#", false);
-        runValidMmi("##43*11#", false);
-        runValidMmi("#33*1234*11#", false);
-        runValidMmi("*21*6505551234**5#", false);
-        runValidMmi("**03**1234*4321*4321#", false);
-        // pound string
-        runValidMmi("5308234092307540923#", true);
-        // short code
-        runValidMmi("22", true);
-        // as part of call setup
-        runValidMmiWithConnect("*31#6505551234");
-
-        // invalid MMI sequences
-        runNotMmi("6505551234");
-        runNotMmi("1234#*12#34566654");
-        runNotMmi("*#*#12#*");
-    }
-
-    private void runValidMmi(String dialString, boolean cancelable) throws CallStateException {
-        Connection c = mGSMPhone.dial(dialString);
-        assertNull(c);
-        Message msg = mGSMTestHandler.waitForMessage(EVENT_MMI_INITIATE);
-        assertNotNull("Message Time Out", msg);
-        // Should not be cancelable.
-        AsyncResult ar = (AsyncResult) msg.obj;
-        MmiCode mmi = (MmiCode) ar.result;
-        assertEquals(cancelable, mmi.isCancelable());
-
-        msg = mGSMTestHandler.waitForMessage(EVENT_MMI_COMPLETE);
-        assertNotNull("Message Time Out", msg);
-    }
-
-    private void runValidMmiWithConnect(String dialString) throws CallStateException {
-        mRadioControl.pauseResponses();
-
-        Connection c = mGSMPhone.dial(dialString);
-        assertNotNull(c);
-
-        hangup(c);
-    }
-
-    private void hangup(Connection cn) throws CallStateException {
-        cn.hangup();
-
-        mRadioControl.resumeResponses();
-        assertNotNull(mGSMTestHandler.waitForMessage(EVENT_DISCONNECT));
-
-    }
-
-    private void runNotMmi(String dialString) throws CallStateException {
-        mRadioControl.pauseResponses();
-
-        Connection c = mGSMPhone.dial(dialString);
-        assertNotNull(c);
-
-        hangup(c);
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
deleted file mode 100644
index 16861fa..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.content.Context;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import com.android.telephony.Rlog;
-
-import com.android.internal.telephony.gsm.GSMPhone;
-import com.android.internal.telephony.test.SimulatedCommands;
-import com.android.internal.telephony.TestPhoneNotifier;
-
-/**
- * This class creates a HandlerThread which waits for the various messages.
- */
-public class GSMTestHandler extends HandlerThread implements Handler.Callback {
-
-    private Handler mHandler;
-    private Message mCurrentMessage;
-
-    private Boolean mMsgConsumed;
-    private SimulatedCommands sc;
-    private GSMPhone mGSMPhone;
-    private Context mContext;
-
-    private static final int FAIL_TIMEOUT_MILLIS = 5 * 1000;
-
-    public GSMTestHandler(Context context) {
-        super("GSMPhoneTest");
-        mMsgConsumed = false;
-        mContext = context;
-   }
-
-    @Override
-    protected void onLooperPrepared() {
-        sc = new SimulatedCommands();
-        mGSMPhone = new GSMPhone(mContext, sc, new TestPhoneNotifier(), true);
-        mHandler = new Handler(getLooper(), this);
-        synchronized (this) {
-            notifyAll();
-        }
-    }
-
-    public boolean handleMessage(Message msg) {
-        synchronized (this) {
-            mCurrentMessage = msg;
-            this.notifyAll();
-            while(!mMsgConsumed) {
-                try {
-                    this.wait();
-                } catch (InterruptedException e) {}
-            }
-            mMsgConsumed = false;
-        }
-        return true;
-    }
-
-
-    public void cleanup() {
-        Looper looper = getLooper();
-        if (looper != null) looper.quit();
-        mHandler = null;
-    }
-
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    public SimulatedCommands getSimulatedCommands() {
-        return sc;
-    }
-
-    public GSMPhone getGSMPhone() {
-        return mGSMPhone;
-    }
-
-    public Message waitForMessage(int code) {
-        Message msg;
-        while(true) {
-            msg = null;
-            synchronized (this) {
-                try {
-                    this.wait(FAIL_TIMEOUT_MILLIS);
-                } catch (InterruptedException e) {
-                }
-
-                // Check if timeout has occurred.
-                if (mCurrentMessage != null) {
-                    // Consume the message
-                    msg = Message.obtain();
-                    msg.copyFrom(mCurrentMessage);
-                    mCurrentMessage = null;
-                    mMsgConsumed = true;
-                    this.notifyAll();
-                }
-            }
-            if (msg == null || code == GSMPhoneTest.ANY_MESSAGE || msg.what == code) return msg;
-       }
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index a847a24..967941c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -55,7 +55,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.telephony.FakeSmsContentProvider;
@@ -192,9 +191,9 @@
                 InboundSmsHandler.SOURCE_NOT_INJECTED);
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
-                anyInt(), anyBoolean(),
-                anyBoolean(), nullable(String.class), nullable(String.class),
-                nullable(String.class), anyBoolean(), anyInt(), anyInt());
+                        anyInt(), anyBoolean(),
+                        anyBoolean(), nullable(String.class), nullable(String.class),
+                        nullable(String.class), anyBoolean(), anyInt(), anyInt());
 
         createInboundSmsTrackerMultiSim();
 
@@ -310,7 +309,6 @@
         processAllMessages();
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testNewSms() {
@@ -330,7 +328,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
@@ -348,7 +345,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testNewSmsWithUserLocked_notificationShown() {
@@ -375,7 +371,6 @@
                 any(Notification.class));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testNewSmsFromBlockedNumberWithUserLocked_noNotificationShown() {
@@ -405,7 +400,6 @@
                 any(Notification.class));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testNewSms_filterInvoked_noBroadcastsSent() {
@@ -431,7 +425,6 @@
                 anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testNewSms_filterChaining_noBroadcastsSent() {
@@ -483,7 +476,6 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testClass0Sms() {
@@ -515,7 +507,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testBroadcastSms() {
@@ -556,7 +547,6 @@
         verifySmsFiltersInvoked(times(2));
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testInjectSms() {
@@ -617,7 +607,6 @@
                 InboundSmsHandler.SOURCE_NOT_INJECTED);
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultiPartSmsWithIncompleteWAP() {
@@ -678,12 +667,13 @@
         assertEquals("IdleState", getCurrentState().getName());
         // verify there are three segments in the db and only one of them is not marked as deleted.
         assertEquals(3, mContentProvider.getNumRows());
-        assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());
 
+        Cursor c = mContentProvider.query(sRawUri, null, "deleted=0", null, null);
+        assertEquals(1, c.getCount());
         verifySmsFiltersInvoked(times(1));
+        c.close();
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testMultiPartSms() {
@@ -757,7 +747,6 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultiPartIncompleteSms() {
@@ -820,9 +809,9 @@
         // State machine should go back to idle
         assertEquals("IdleState", getCurrentState().getName());
         verifySmsFiltersInvoked(never());
+        c.close();
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultiPartSmsWithInvalidSeqNumber() {
@@ -882,7 +871,6 @@
         verifySmsFiltersInvoked(never());
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() {
@@ -921,7 +909,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultipartSmsFromBlockedEmail_noBroadcastsSent() {
@@ -977,7 +964,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testMultipartSms_filterInvoked_noBroadcastsSent() {
@@ -1027,7 +1013,6 @@
                 anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testBroadcastUndeliveredUserLocked() throws Exception {
@@ -1085,7 +1070,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testBroadcastUndeliveredUserUnlocked() throws Exception {
@@ -1123,7 +1107,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testBroadcastUndeliveredDeleted() throws Exception {
@@ -1164,7 +1147,6 @@
         verifySmsFiltersInvoked(never());
     }
 
-    @FlakyTest
     @Test
     @MediumTest
     public void testBroadcastUndeliveredMultiPart() throws Exception {
@@ -1180,7 +1162,7 @@
         //return InboundSmsTracker objects corresponding to the 2 parts
         doReturn(mInboundSmsTrackerPart1).doReturn(mInboundSmsTrackerPart2).
                 when(mTelephonyComponentFactory).makeInboundSmsTracker(any(Context.class),
-                any(Cursor.class), anyBoolean());
+                        any(Cursor.class), anyBoolean());
 
         SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
         // wait for ScanRawTableThread
@@ -1191,7 +1173,6 @@
         verifySmsFiltersInvoked(times(1));
     }
 
-    @FlakyTest // temporarily disabled, see b/182498318
     @Test
     @MediumTest
     public void testBroadcastUndeliveredMultiSim() throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
index 8c1caa5..70df8aa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.gsm;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertTrue;
@@ -36,6 +38,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.concurrent.Executor;
 
 /**
@@ -102,6 +105,209 @@
         }
     }
 
+    // The main purpose of this test is to list all the valid use cases of getControlStrings().
+    @Test
+    public void testGetControlStrings() {
+        // Test if controlStrings list is empty when inputs are null
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(null,
+                null);
+        assertThat(controlStrings).isEmpty();
+
+        // Test control strings of Call Forwarding Unconditional
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CFU);
+        assertThat(controlStrings).containsExactly("*#21", "*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CFU);
+        assertThat(controlStrings).containsExactly("**21", "**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CFU);
+        assertThat(controlStrings).containsExactly("#21", "#002");
+
+        // Test control strings of Call Forwarding Busy
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CF_BUSY);
+        assertThat(controlStrings).containsExactly("*#67", "*#004", "*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CF_BUSY);
+        assertThat(controlStrings).containsExactly("**67", "**004", "**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CF_BUSY);
+        assertThat(controlStrings).containsExactly("#67", "#004", "#002");
+
+        // Test control strings of Call Forwarding No Reply
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CF_NO_REPLY);
+        assertThat(controlStrings).containsExactly("*#61", "*#004", "*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CF_NO_REPLY);
+        assertThat(controlStrings).containsExactly("**61", "**004", "**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CF_NO_REPLY);
+        assertThat(controlStrings).containsExactly("#61", "#004", "#002");
+
+        // Test control strings of Call Forwarding Not Reachable
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CF_NOT_REACHABLE);
+        assertThat(controlStrings).containsExactly("*#62", "*#004", "*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CF_NOT_REACHABLE);
+        assertThat(controlStrings).containsExactly("**62", "**004", "**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CF_NOT_REACHABLE);
+        assertThat(controlStrings).containsExactly("#62", "#004", "#002");
+
+        // Test control strings of Call Forwarding All
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CF_ALL);
+        assertThat(controlStrings).containsExactly("*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CF_ALL);
+        assertThat(controlStrings).containsExactly("**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CF_ALL);
+        assertThat(controlStrings).containsExactly("#002");
+
+        // Test control strings of Call Forwarding ALl Conditional
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+        assertThat(controlStrings).containsExactly("*#004", "*#002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_REGISTRATION,
+                SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+        assertThat(controlStrings).containsExactly("**004", "**002");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CF_ALL_CONDITIONAL);
+        assertThat(controlStrings).containsExactly("#004", "#002");
+
+        // Test control strings of CLIP
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CLIP);
+        assertThat(controlStrings).containsExactly("*#30");
+
+        // Test control strings of CLIR
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_CLIR);
+        assertThat(controlStrings).containsExactly("*#31");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_CLIR);
+        assertThat(controlStrings).containsExactly("*31");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_CLIR);
+        assertThat(controlStrings).containsExactly("#31");
+
+        // Test control strings of Call Waiting
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_WAIT);
+        assertThat(controlStrings).containsExactly("*#43");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_WAIT);
+        assertThat(controlStrings).containsExactly("*43");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_WAIT);
+        assertThat(controlStrings).containsExactly("#43");
+
+        // Test control strings of BAOC
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_BAOC);
+        assertThat(controlStrings).containsExactly("*#33", "*#330", "*#333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_BAOC);
+        assertThat(controlStrings).containsExactly("*33", "*330", "*333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_BAOC);
+        assertThat(controlStrings).containsExactly("#33", "#330", "#333");
+
+        // Test control strings of BAOIC
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_BAOIC);
+        assertThat(controlStrings).containsExactly("*#331", "*#330", "*#333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_BAOIC);
+        assertThat(controlStrings).containsExactly("*331", "*330", "*333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_BAOIC);
+        assertThat(controlStrings).containsExactly("#331", "#330", "#333");
+
+        // Test control strings of BAOICxH
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_BAOIC_EXC_HOME);
+        assertThat(controlStrings).containsExactly("*#332", "*#330", "*#333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_BAOIC_EXC_HOME);
+        assertThat(controlStrings).containsExactly("*332", "*330", "*333");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_BAOIC_EXC_HOME);
+        assertThat(controlStrings).containsExactly("#332", "#330", "#333");
+
+        // Test control strings of BAIC
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_BAIC);
+        assertThat(controlStrings).containsExactly("*#35", "*#330", "*#353");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_BAIC);
+        assertThat(controlStrings).containsExactly("*35", "*330", "*353");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_BAIC);
+        assertThat(controlStrings).containsExactly("#35", "#330", "#353");
+
+        // Test control strings of BAICr
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_BAIC_ROAMING);
+        assertThat(controlStrings).containsExactly("*#351", "*#330", "*#353");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_BAIC_ROAMING);
+        assertThat(controlStrings).containsExactly("*351", "*330", "*353");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_BAIC_ROAMING);
+        assertThat(controlStrings).containsExactly("#351", "#330", "#353");
+
+        // Test control strings of BA_ALL
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_ALL_BARRING);
+        assertThat(controlStrings).containsExactly("*#330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_ALL_BARRING);
+        assertThat(controlStrings).containsExactly("*330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_ALL_BARRING);
+        assertThat(controlStrings).containsExactly( "#330");
+
+        // Test control strings of BA_MO
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_OUTGOING_BARRING);
+        assertThat(controlStrings).containsExactly("*#333", "*#330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_OUTGOING_BARRING);
+        assertThat(controlStrings).containsExactly("*333", "*330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_OUTGOING_BARRING);
+        assertThat(controlStrings).containsExactly( "#333", "#330");
+
+        // Test control strings of BA_MT
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_INTERROGATION,
+                SsData.ServiceType.SS_INCOMING_BARRING);
+        assertThat(controlStrings).containsExactly("*#353", "*#330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_ACTIVATION,
+                SsData.ServiceType.SS_INCOMING_BARRING);
+        assertThat(controlStrings).containsExactly("*353", "*330");
+        controlStrings = GsmMmiCode.getControlStrings(SsData.RequestType.SS_DEACTIVATION,
+                SsData.ServiceType.SS_INCOMING_BARRING);
+        assertThat(controlStrings).containsExactly( "#353", "#330");
+    }
+
+    @Test
+    public void testGetControlStringsForPwd() {
+        // Test if controlStrings list is empty when inputs are null
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd(null,
+                null);
+        assertThat(controlStrings).isEmpty();
+
+        // Test control strings of Call Barring Change Password
+        controlStrings = GsmMmiCode.getControlStringsForPwd(
+                SsData.RequestType.SS_REGISTRATION, SsData.ServiceType.SS_ALL_BARRING);
+        assertThat(controlStrings).containsExactly("**03*330");
+    }
+
     private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 19cacf0..4bf5415 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -66,9 +66,12 @@
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsDispatchersController;
+import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
 import com.android.internal.telephony.TestApplication;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.IsimUiccRecords;
 
 import org.junit.After;
 import org.junit.Before;
@@ -228,7 +231,8 @@
         restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
         restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
         Context realContext = TestApplication.getAppContext();
-        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
+        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT),
+                Context.RECEIVER_EXPORTED);
     }
 
     @Test
@@ -373,6 +377,7 @@
 
     @Test
     @SmallTest
+    @Ignore("b/256282780")
     public void testSendSmsByCarrierApp() throws Exception {
         mockCarrierApp();
         mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
@@ -447,6 +452,7 @@
 
     @Test
     @SmallTest
+    @Ignore("b/256282780")
     public void testSendMultipartSmsByCarrierApp() throws Exception {
         mockCarrierApp();
         mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
@@ -503,4 +509,74 @@
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
                 any(Message.class));
     }
+
+
+    @Test
+    public void testSendTextWithMessageRef() throws Exception {
+        int messageRef = mGsmSmsDispatcher.nextMessageRef() + 1;
+        mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+                null, null, null, null, false, -1, false, -1, false, 0L);
+
+        ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+                any(Message.class));
+        byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+        assertEquals(messageRef, pdu[1]);
+    }
+
+    @Test
+    public void testSendMultipartWithMessageRef() throws Exception {
+        ArrayList<String> parts = new ArrayList<>();
+        parts.add("segment1");
+        parts.add("segment2");
+        parts.add("segment3");
+        int messageRef = mGsmSmsDispatcher.nextMessageRef() + parts.size();
+        mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
+                null, null, null, null, false, -1, false, -1, 0L);
+        waitForMs(150);
+        ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+
+        verify(mSimulatedCommandsVerifier, times(parts.size() - 1)).sendSMSExpectMore(anyString(),
+                anyString(),
+                any(Message.class));
+        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+                any(Message.class));
+        byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+        assertEquals(messageRef, pdu[1]);
+    }
+
+    @Test
+    public void testSendTextWithMessageRefNegativeBoundaryCondition() throws Exception {
+        mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
+                mSimulatedCommands);
+        doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
+        Message msg = mGsmSmsDispatcher.obtainMessage(17);
+        mPhone.getIccRecords().setSmssTpmrValue(-1, msg);
+        SubscriptionController.getInstance().updateMessageRef(mPhone.getSubId(), -1);
+        mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+                null, null, null, null, false, -1, false, -1, false, 0L);
+
+        ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+                any(Message.class));
+        byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+        assertEquals(0, pdu[1]);
+    }
+
+    @Test
+    public void testSendTextWithMessageRefMaxBoundaryCondition() throws Exception {
+        mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
+                mSimulatedCommands);
+        doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
+        Message msg = mGsmSmsDispatcher.obtainMessage(17);
+        mPhone.getIccRecords().setSmssTpmrValue(255, msg);
+        mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
+                null, null, null, null, false, -1, false, -1, false, 0L);
+
+        ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
+        verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
+                any(Message.class));
+        byte[] pdu = IccUtils.hexStringToBytes(pduCaptor.getValue());
+        assertEquals(0, pdu[1]);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
deleted file mode 100644
index a9d869c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.content.Context;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.os.SystemClock;
-import com.android.telephony.Rlog;
-
-import com.android.internal.telephony.BaseCommands;
-import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.uicc.IccIoResult;
-import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
-
-import junit.framework.Assert;
-
-/**
- * Dummy BaseCommands for UsimDataDownloadTest. Only implements UICC envelope and
- * SMS acknowledgement commands.
- */
-class UsimDataDownloadCommands extends BaseCommands {
-    private static final String TAG = "UsimDataDownloadCommands";
-
-    private boolean mExpectingAcknowledgeGsmSms;        // true if expecting ack GSM SMS
-    private boolean mExpectingAcknowledgeGsmSmsSuccess; // true if expecting ack SMS success
-    private int mExpectingAcknowledgeGsmSmsFailureCause;    // expecting ack SMS failure cause
-    private String mExpectingAcknowledgeGsmSmsPdu;          // expecting ack SMS PDU
-
-    private boolean mExpectingSendEnvelope;         // true to expect a send envelope command
-    private String mExpectingSendEnvelopeContents;  // expected string for send envelope
-    private int mExpectingSendEnvelopeResponseSw1;  // SW1/SW2 response status
-    private int mExpectingSendEnvelopeResponseSw2;  // SW1/SW2 response status
-    private String mExpectingSendEnvelopeResponse;  // Response string for Send Envelope
-
-    UsimDataDownloadCommands(Context context) {
-        super(context);
-    }
-
-    /**
-     * Expect a call to acknowledgeLastIncomingGsmSms with success flag and failure cause.
-     * @param success true if expecting success; false if expecting failure
-     * @param cause the failure cause, if success is false
-     */
-    synchronized void expectAcknowledgeGsmSms(boolean success, int cause) {
-        Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
-        mExpectingAcknowledgeGsmSms = true;
-        mExpectingAcknowledgeGsmSmsSuccess = success;
-        mExpectingAcknowledgeGsmSmsFailureCause = cause;
-    }
-
-    /**
-     * Expect a call to acknowledgeLastIncomingGsmSmsWithPdu with success flag and PDU.
-     * @param success true if expecting success; false if expecting failure
-     * @param ackPdu the acknowledgement PDU to expect
-     */
-    synchronized void expectAcknowledgeGsmSmsWithPdu(boolean success, String ackPdu) {
-        Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
-        mExpectingAcknowledgeGsmSms = true;
-        mExpectingAcknowledgeGsmSmsSuccess = success;
-        mExpectingAcknowledgeGsmSmsPdu = ackPdu;
-    }
-
-    /**
-     * Expect a call to sendEnvelopeWithStatus().
-     * @param contents expected envelope contents to send
-     * @param sw1 simulated SW1 status to return
-     * @param sw2 simulated SW2 status to return
-     * @param response simulated envelope response to return
-     */
-    synchronized void expectSendEnvelope(String contents, int sw1, int sw2, String response) {
-        Assert.assertFalse("expectSendEnvelope called twice", mExpectingSendEnvelope);
-        mExpectingSendEnvelope = true;
-        mExpectingSendEnvelopeContents = contents;
-        mExpectingSendEnvelopeResponseSw1 = sw1;
-        mExpectingSendEnvelopeResponseSw2 = sw2;
-        mExpectingSendEnvelopeResponse = response;
-    }
-
-    synchronized void assertExpectedMethodsCalled() {
-        long stopTime = SystemClock.elapsedRealtime() + 5000;
-        while ((mExpectingAcknowledgeGsmSms || mExpectingSendEnvelope)
-                && SystemClock.elapsedRealtime() < stopTime) {
-            try {
-                wait();
-            } catch (InterruptedException ignored) {}
-        }
-        Assert.assertFalse("expecting SMS acknowledge call", mExpectingAcknowledgeGsmSms);
-        Assert.assertFalse("expecting send envelope call", mExpectingSendEnvelope);
-    }
-
-    @Override
-    public synchronized void acknowledgeLastIncomingGsmSms(boolean success, int cause,
-            Message response) {
-        Rlog.d(TAG, "acknowledgeLastIncomingGsmSms: success=" + success + ", cause=" + cause);
-        Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
-        Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
-        Assert.assertEquals(mExpectingAcknowledgeGsmSmsFailureCause, cause);
-        mExpectingAcknowledgeGsmSms = false;
-        if (response != null) {
-            AsyncResult.forMessage(response);
-            response.sendToTarget();
-        }
-        notifyAll();    // wake up assertExpectedMethodsCalled()
-    }
-
-    @Override
-    public synchronized void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu,
-            Message response) {
-        Rlog.d(TAG, "acknowledgeLastIncomingGsmSmsWithPdu: success=" + success
-                + ", ackPDU= " + ackPdu);
-        Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
-        Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
-        Assert.assertEquals(mExpectingAcknowledgeGsmSmsPdu, ackPdu);
-        mExpectingAcknowledgeGsmSms = false;
-        if (response != null) {
-            AsyncResult.forMessage(response);
-            response.sendToTarget();
-        }
-        notifyAll();    // wake up assertExpectedMethodsCalled()
-    }
-
-    @Override
-    public synchronized void sendEnvelopeWithStatus(String contents, Message response) {
-        // Add spaces between hex bytes for readability
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < contents.length(); i += 2) {
-            builder.append(contents.charAt(i)).append(contents.charAt(i+1)).append(' ');
-        }
-        Rlog.d(TAG, "sendEnvelopeWithStatus: " + builder.toString());
-
-        Assert.assertTrue("unexpected call to send envelope", mExpectingSendEnvelope);
-        Assert.assertEquals(mExpectingSendEnvelopeContents, contents);
-        mExpectingSendEnvelope = false;
-
-        IccIoResult result = new IccIoResult(mExpectingSendEnvelopeResponseSw1,
-                mExpectingSendEnvelopeResponseSw2, mExpectingSendEnvelopeResponse);
-
-        if (response != null) {
-            AsyncResult.forMessage(response, result, null);
-            response.sendToTarget();
-        }
-        notifyAll();    // wake up assertExpectedMethodsCalled()
-    }
-
-    @Override
-    public void setSuppServiceNotifications(boolean enable, Message result) {
-    }
-
-    @Override
-    public void supplyIccPin(String pin, Message result) {
-    }
-
-    @Override
-    public void supplyIccPinForApp(String pin, String aid, Message result) {
-    }
-
-    @Override
-    public void supplyIccPuk(String puk, String newPin, Message result) {
-    }
-
-    @Override
-    public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
-    }
-
-    @Override
-    public void supplyIccPin2(String pin2, Message result) {
-    }
-
-    @Override
-    public void supplyIccPin2ForApp(String pin2, String aid, Message result) {
-    }
-
-    @Override
-    public void supplyIccPuk2(String puk2, String newPin2, Message result) {
-    }
-
-    @Override
-    public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result) {
-    }
-
-    @Override
-    public void changeIccPin(String oldPin, String newPin, Message result) {
-    }
-
-    @Override
-    public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result) {
-    }
-
-    @Override
-    public void changeIccPin2(String oldPin2, String newPin2, Message result) {
-    }
-
-    @Override
-    public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr, Message result) {
-    }
-
-    @Override
-    public void changeBarringPassword(String facility, String oldPwd, String newPwd,
-            Message result) {
-    }
-
-    @Override
-    public void supplyNetworkDepersonalization(String netpin, Message result) {
-    }
-
-    @Override
-    public void getCurrentCalls(Message result) {
-    }
-
-    @Override
-    public void getPDPContextList(Message result) {
-    }
-
-    @Override
-    public void getDataCallList(Message result) {
-    }
-
-    @Override
-    public void dial(String address, int clirMode, Message result) {
-    }
-
-    @Override
-    public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
-    }
-
-    @Override
-    public void getIMSI(Message result) {
-    }
-
-    @Override
-    public void getIMEI(Message result) {
-    }
-
-    @Override
-    public void getIMEISV(Message result) {
-    }
-
-    @Override
-    public void hangupConnection(int gsmIndex, Message result) {
-    }
-
-    @Override
-    public void hangupWaitingOrBackground(Message result) {
-    }
-
-    @Override
-    public void hangupForegroundResumeBackground(Message result) {
-    }
-
-    @Override
-    public void switchWaitingOrHoldingAndActive(Message result) {
-    }
-
-    @Override
-    public void conference(Message result) {
-    }
-
-    @Override
-    public void setPreferredVoicePrivacy(boolean enable, Message result) {
-    }
-
-    @Override
-    public void getPreferredVoicePrivacy(Message result) {
-    }
-
-    @Override
-    public void separateConnection(int gsmIndex, Message result) {
-    }
-
-    @Override
-    public void acceptCall(Message result) {
-    }
-
-    @Override
-    public void rejectCall(Message result) {
-    }
-
-    @Override
-    public void explicitCallTransfer(Message result) {
-    }
-
-    @Override
-    public void getLastCallFailCause(Message result) {
-    }
-
-    @Override
-    public void getLastPdpFailCause(Message result) {
-    }
-
-    @Override
-    public void getLastDataCallFailCause(Message result) {
-    }
-
-    @Override
-    public void setMute(boolean enableMute, Message response) {
-    }
-
-    @Override
-    public void getMute(Message response) {
-    }
-
-    @Override
-    public void getSignalStrength(Message response) {
-    }
-
-    @Override
-    public void getVoiceRegistrationState(Message response) {
-    }
-
-    @Override
-    public void getDataRegistrationState(Message response) {
-    }
-
-    @Override
-    public void getOperator(Message response) {
-    }
-
-    @Override
-    public void sendDtmf(char c, Message result) {
-    }
-
-    @Override
-    public void startDtmf(char c, Message result) {
-    }
-
-    @Override
-    public void stopDtmf(Message result) {
-    }
-
-    @Override
-    public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
-    }
-
-    @Override
-    public void sendSMS(String smscPDU, String pdu, Message response) {
-    }
-
-    @Override
-    public void sendSMSExpectMore(String smscPDU, String pdu, Message response) {
-    }
-
-    @Override
-    public void sendCdmaSms(byte[] pdu, Message response) {
-    }
-
-    @Override
-    public void sendImsGsmSms (String smscPDU, String pdu,
-            int retry, int messageRef, Message response) {
-    }
-
-    @Override
-    public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
-            Message response) {
-    }
-
-    @Override
-    public void deleteSmsOnSim(int index, Message response) {
-    }
-
-    @Override
-    public void deleteSmsOnRuim(int index, Message response) {
-    }
-
-    @Override
-    public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
-    }
-
-    @Override
-    public void writeSmsToRuim(int status, byte[] pdu, Message response) {
-    }
-
-    @Override
-    public void setRadioPower(boolean on, Message response) {
-    }
-
-    @Override
-    public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response) {
-    }
-
-    @Override
-    public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
-            String pin2, Message response) {
-    }
-
-    @Override
-    public void queryCLIP(Message response) {
-    }
-
-    @Override
-    public void getCLIR(Message response) {
-    }
-
-    @Override
-    public void setCLIR(int clirMode, Message response) {
-    }
-
-    @Override
-    public void queryCallWaiting(int serviceClass, Message response) {
-    }
-
-    @Override
-    public void setCallWaiting(boolean enable, int serviceClass, Message response) {
-    }
-
-    @Override
-    public void setCallForward(int action, int cfReason, int serviceClass, String number,
-            int timeSeconds, Message response) {
-    }
-
-    @Override
-    public void queryCallForwardStatus(int cfReason, int serviceClass, String number,
-            Message response) {
-    }
-
-    @Override
-    public void setNetworkSelectionModeAutomatic(Message response) {
-    }
-
-    @Override
-    public void setNetworkSelectionModeManual(String operatorNumeric, Message response) {
-    }
-
-    @Override
-    public void getNetworkSelectionMode(Message response) {
-    }
-
-    @Override
-    public void getAvailableNetworks(Message response) {
-    }
-
-    @Override
-    public void getBasebandVersion(Message response) {
-    }
-
-    @Override
-    public void queryFacilityLock(String facility, String password, int serviceClass,
-            Message response) {
-    }
-
-    @Override
-    public void queryFacilityLockForApp(String facility, String password, int serviceClass,
-            String appId, Message response) {
-    }
-
-    @Override
-    public void setFacilityLock(String facility, boolean lockState, String password,
-            int serviceClass, Message response) {
-    }
-
-    @Override
-    public void setFacilityLockForApp(String facility, boolean lockState, String password,
-            int serviceClass, String appId, Message response) {
-    }
-
-    @Override
-    public void sendUSSD(String ussdString, Message response) {
-    }
-
-    @Override
-    public void cancelPendingUssd(Message response) {
-    }
-
-    @Override
-    public void resetRadio(Message result) {
-    }
-
-    @Override
-    public void setBandMode(int bandMode, Message response) {
-    }
-
-    @Override
-    public void queryAvailableBandMode(Message response) {
-    }
-
-    @Override
-    public void setPreferredNetworkType(int networkType, Message response) {
-    }
-
-    @Override
-    public void getPreferredNetworkType(Message response) {
-    }
-
-    @Override
-    public void setLocationUpdates(boolean enable, Message response) {
-    }
-
-    @Override
-    public void getSmscAddress(Message result) {
-    }
-
-    @Override
-    public void setSmscAddress(String address, Message result) {
-    }
-
-    @Override
-    public void reportSmsMemoryStatus(boolean available, Message result) {
-    }
-
-    @Override
-    public void reportStkServiceIsRunning(Message result) {
-    }
-
-    @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-    }
-
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-    }
-
-    @Override
-    public void sendTerminalResponse(String contents, Message response) {
-    }
-
-    @Override
-    public void sendEnvelope(String contents, Message response) {
-    }
-
-    @Override
-    public void handleCallSetupRequestFromSim(boolean accept, Message response) {
-    }
-
-    @Override
-    public void setGsmBroadcastActivation(boolean activate, Message result) {
-    }
-
-    @Override
-    public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
-    }
-
-    @Override
-    public void getGsmBroadcastConfig(Message response) {
-    }
-
-    @Override
-    public void getDeviceIdentity(Message response) {
-    }
-
-    @Override
-    public void getCDMASubscription(Message response) {
-    }
-
-    @Override
-    public void getImsRegistrationState (Message result) {
-    }
-
-    @Override
-    public void sendCDMAFeatureCode(String FeatureCode, Message response) {
-    }
-
-    @Override
-    public void setPhoneType(int phoneType) {
-    }
-
-    @Override
-    public void queryCdmaRoamingPreference(Message response) {
-    }
-
-    @Override
-    public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
-    }
-
-    @Override
-    public void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response) {
-    }
-
-    @Override
-    public void getCdmaSubscriptionSource(Message response) {
-    }
-
-    @Override
-    public void setTTYMode(int ttyMode, Message response) {
-    }
-
-    @Override
-    public void queryTTYMode(Message response) {
-    }
-
-    @Override
-    public void setupDataCall(String radioTechnology, String profile, String apn, String user,
-            String password, String authType, String protocol, Message result) {
-    }
-
-    @Override
-    public void deactivateDataCall(int cid, int reason, Message result) {
-    }
-
-    @Override
-    public void setCdmaBroadcastActivation(boolean activate, Message result) {
-    }
-
-    @Override
-    public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message response) {
-    }
-
-    @Override
-    public void getCdmaBroadcastConfig(Message result) {
-    }
-
-    @Override
-    public void exitEmergencyCallbackMode(Message response) {
-    }
-
-    @Override
-    public void getIccCardStatus(Message result) {
-    }
-
-    @Override
-    public void requestIsimAuthentication(String nonce, Message response) {
-    }
-
-    @Override
-    public void requestIccSimAuthentication(String data, Message response) {
-    }
-
-    @Override
-    public void getVoiceRadioTechnology(Message response) {
-    }
-
-    @Override
-    public void getCellInfoList(Message result) {
-    }
-
-    @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
-    }
-
-    @Override
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result) {
-    }
-
-    @Override
-    public void setDataProfile(DataProfile[] dps, Message result) {
-    }
-
-    @Override
-    public void getIMSIForApp(String aid, Message result) {
-    }
-
-    @Override
-    public void iccIOForApp(int command, int fileid, String path, int p1, int p2, int p3,
-            String data, String pin2, String aid, Message response) {
-    }
-
-    @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {
-    }
-
-    @Override
-    public void iccCloseLogicalChannel(int channel, Message response) {
-    }
-
-    @Override
-    public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
-            int p1, int p2, int p3, String data, Message response) {
-    }
-
-    @Override
-    public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
-            int p3, String data, Message response) {
-    }
-
-    @Override
-    public void nvReadItem(int itemID, Message response) {
-    }
-
-    @Override
-    public void nvWriteItem(int itemID, String itemValue, Message response) {
-    }
-
-    @Override
-    public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) {
-    }
-
-    @Override
-    public void nvResetConfig(int resetType, Message response) {
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
deleted file mode 100644
index 9fbb86c..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.gsm;
-
-import android.os.HandlerThread;
-import android.test.AndroidTestCase;
-import com.android.telephony.Rlog;
-
-import java.nio.charset.Charset;
-
-/**
- * Test SMS-PP data download to UICC.
- * Uses test messages from 3GPP TS 31.124 section 27.22.5.
- */
-public class UsimDataDownloadTest extends AndroidTestCase {
-    private static final String TAG = "UsimDataDownloadTest";
-
-    class TestHandlerThread extends HandlerThread {
-        private UsimDataDownloadHandler mHandler;
-
-        TestHandlerThread() {
-            super("TestHandlerThread");
-        }
-
-        @Override
-        protected void onLooperPrepared() {
-            synchronized (this) {
-                mHandler = new UsimDataDownloadHandler(mCi, 0);
-                notifyAll();
-            }
-        }
-
-        UsimDataDownloadHandler getHandler() {
-            synchronized (this) {
-                while (mHandler == null) {
-                    try {
-                        wait();
-                    } catch (InterruptedException ignored) {}
-                }
-                return mHandler;
-            }
-        }
-    }
-
-    private UsimDataDownloadCommands mCi;
-    private TestHandlerThread mHandlerThread;
-    UsimDataDownloadHandler mHandler;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mCi = new UsimDataDownloadCommands(mContext);
-        mHandlerThread = new TestHandlerThread();
-        mHandlerThread.start();
-        mHandler = mHandlerThread.getHandler();
-        Rlog.d(TAG, "mHandler is constructed");
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mHandlerThread.quit();
-        super.tearDown();
-    }
-
-    // SMS-PP Message 3.1.1
-    private static final byte[] SMS_PP_MESSAGE_3_1_1 = {
-            // Service center address
-            0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
-
-            0x04, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, 0x16, (byte) 0x89, 0x10, 0x10, 0x00, 0x00,
-            0x00, 0x00, 0x0d, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
-            0x67, 0x65, 0x20, 0x31
-    };
-
-    // SMS-PP Download Envelope 3.1.1
-    private static final String SMS_PP_ENVELOPE_3_1_1 = "d12d8202838106099111223344556677f88b1c04"
-            + "049121437f16891010000000000d546573744d6573736167652031";
-
-    // SMS-PP Message 3.1.5
-    private static final byte[] SMS_PP_MESSAGE_3_1_5 = {
-            // Service center address
-            0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
-
-            0x44, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, (byte) 0xf6, (byte) 0x89, 0x10, 0x10, 0x00,
-            0x00, 0x00, 0x00, 0x1e, 0x02, 0x70, 0x00, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x00,
-            0x00, 0x00, (byte) 0xbf, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-            (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc,
-            (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc
-    };
-
-    // SMS-PP Download Envelope 3.1.5
-    private static final String SMS_PP_ENVELOPE_3_1_5 = "d13e8202838106099111223344556677f88b2d44"
-            + "049121437ff6891010000000001e0270000019000d00000000bfff00000000000100"
-            + "dcdcdcdcdcdcdcdcdcdc";
-
-    public void testDataDownloadMessage1() {
-        SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_1);
-        assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
-
-        mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "");
-        mCi.expectAcknowledgeGsmSms(true, 0);
-        mHandler.startDataDownload(message);
-        mCi.assertExpectedMethodsCalled();
-
-        mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "0123456789");
-        mCi.expectAcknowledgeGsmSmsWithPdu(true, "00077f16050123456789");
-        mHandler.startDataDownload(message);
-        mCi.assertExpectedMethodsCalled();
-
-        mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x62, 0xff, "0123456789abcdef");
-        mCi.expectAcknowledgeGsmSmsWithPdu(false, "00d5077f16080123456789abcdef");
-        mHandler.startDataDownload(message);
-        mCi.assertExpectedMethodsCalled();
-    }
-
-    public void testDataDownloadMessage5() {
-        SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_5);
-        assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
-
-        mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x90, 0x00, "9876543210");
-        mCi.expectAcknowledgeGsmSmsWithPdu(true, "00077ff6059876543210");
-        mHandler.startDataDownload(message);
-        mCi.assertExpectedMethodsCalled();
-
-        mCi.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x93, 0x00, "");
-        mCi.expectAcknowledgeGsmSms(false, 0xd4);   // SIM toolkit busy
-        mHandler.startDataDownload(message);
-        mCi.assertExpectedMethodsCalled();
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java
new file mode 100644
index 0000000..7c45f24
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsEnablementTrackerTest.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.ims;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for ImsEnablementTracker
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsEnablementTrackerTest extends ImsTestBase {
+
+    private static final int SLOT_1 = 1;
+    private static final int SUB_1 = 11;
+
+    private static final long TEST_REQUEST_THROTTLE_TIME_MS = 1000L;
+    // Mocked classes
+    @Mock
+    IImsServiceController mMockServiceControllerBinder;
+
+    private TestableImsEnablementTracker mImsEnablementTracker;
+    private Handler mHandler;
+
+
+    private static class TestableImsEnablementTracker extends ImsEnablementTracker {
+        private long mLastImsOperationTimeMs = 0L;
+        TestableImsEnablementTracker(Looper looper, IImsServiceController controller, int state) {
+            super(looper, controller, state);
+        }
+
+        @Override
+        protected long getLastOperationTimeMillis() {
+            return mLastImsOperationTimeMs;
+        }
+
+        private void setLastOperationTimeMillis(long timeMills) {
+            mLastImsOperationTimeMs = timeMills;
+        }
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        // Make sure the handler is empty before finishing the test.
+        waitForHandlerAction(mHandler, TEST_REQUEST_THROTTLE_TIME_MS);
+        mImsEnablementTracker = null;
+        super.tearDown();
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandInDefaultState() throws RemoteException {
+        // Verify that when the enable command is received in the Default state and enableIms
+        // is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DEFAULT);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+         // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisableCommandInDefaultState() throws RemoteException {
+        // Verify that when the disable command is received in the Default state and disableIms
+        // is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DEFAULT);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResetCommandInDefaultState() throws RemoteException {
+        // Verify that when reset command is received in the Default state, it should be ignored.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DEFAULT);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DEFAULT));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandInEnabledState() throws RemoteException {
+        // Verify that received the enable command is not handle in the Enabled state,
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder, never()).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisableCommandInEnabledState() throws RemoteException {
+        // Verify that when the disable command is received in the Enabled state and disableIms
+        // is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResetCommandInEnabledState() throws RemoteException {
+        // Verify that when the reset command is received in the Enabled state and disableIms
+        // and enableIms are called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+        // The disableIms was called. So set the last operation time to current.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        waitForHandlerActionDelayed(mHandler, TEST_REQUEST_THROTTLE_TIME_MS,
+                TEST_REQUEST_THROTTLE_TIME_MS + 100);
+        verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisableCommandInDisabledState() throws RemoteException {
+        // Verify that when disable command is received in the Disabled state, it should be ignored.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandInDisabledState() throws RemoteException {
+        // Verify that when the enable command is received in the Disabled state and enableIms
+        // is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandWithoutTimeoutInDisableState() throws RemoteException {
+        // Verify that when the enable command is received in the Disabled state. After throttle
+        // time expired, the enableIms is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResetCommandInDisabledState() throws RemoteException {
+        // Verify that the reset command is received in the Disabled state and it`s not handled.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandInDisablingState() throws RemoteException {
+        // Verify that when enable command is received in the Disabling state, it should be ignored.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisablingMessageInDisablingState() throws RemoteException {
+        // Verify that when the internal disable message is received in the Disabling state and
+        // disableIms is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).disableIms(anyInt(), anyInt());
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResetCommandInDisablingState() throws RemoteException {
+        // Verify when the reset command is received in the Disabling state the disableIms and
+        // enableIms are called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+        // The disableIms was called. So set the last operation time to current.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnablingMessageInEnablingState() throws RemoteException {
+        // Verify that when the internal enable message is received in the Enabling state and
+        // enableIms is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLING);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder).enableIms(anyInt(), anyInt());
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisableCommandInEnablingState() throws RemoteException {
+        // Verify that when the disable command is received in the Enabling state and
+        // clear pending message and disableIms is not called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verify(mMockServiceControllerBinder, never()).disableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResetCommandWithEnablingState() throws RemoteException {
+        // Verify that when reset command is received in the Enabling state, it should be ignored.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLING));
+    }
+
+    @SmallTest
+    @Test
+    public void testEnableCommandInResettingState() throws RemoteException {
+        // Verify that when the enable command is received in the Resetting state and
+        // enableIms is not called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_RESETTING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        verifyZeroInteractions(mMockServiceControllerBinder);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_RESETTING));
+    }
+
+    @SmallTest
+    @Test
+    public void testDisableCommandInResettingState() throws RemoteException {
+        // Verify that when the disable command is received in the Resetting state and
+        // disableIms is called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_RESETTING);
+        mHandler = mImsEnablementTracker.getHandler();
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.startStateMachineAsConnected();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).disableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testResettingMessageInResettingState() throws RemoteException {
+        // Verify that when the internal reset message is received in the Resetting state and
+        // disableIms and enableIms are called.
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_RESETTING);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        verify(mMockServiceControllerBinder).disableIms(anyInt(), anyInt());
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder).enableIms(anyInt(), anyInt());
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testConsecutiveCommandInEnabledState() throws RemoteException {
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_ENABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLING));
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLING));
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder, times(1)).disableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+    }
+
+    @SmallTest
+    @Test
+    public void testConsecutiveCommandInDisabledState() throws RemoteException {
+        mImsEnablementTracker = createTracker(mMockServiceControllerBinder,
+                mImsEnablementTracker.STATE_IMS_DISABLED);
+        mImsEnablementTracker.startStateMachineAsConnected();
+        mHandler = mImsEnablementTracker.getHandler();
+        // Wait for a while for the state machine to be ready.
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+
+        // Set the last operation time to current to verify the message with delay.
+        mImsEnablementTracker.setLastOperationTimeMillis(System.currentTimeMillis());
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLING));
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+
+        mImsEnablementTracker.resetIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+
+        mImsEnablementTracker.disableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, 100, 150);
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_DISABLED));
+
+        mImsEnablementTracker.enableIms(SLOT_1, SUB_1);
+        waitForHandlerActionDelayed(mHandler, mImsEnablementTracker.getRemainThrottleTime(),
+                mImsEnablementTracker.getRemainThrottleTime() + 100);
+        verify(mMockServiceControllerBinder, times(1)).enableIms(eq(SLOT_1), eq(SUB_1));
+        assertTrue(mImsEnablementTracker.isState(mImsEnablementTracker.STATE_IMS_ENABLED));
+    }
+
+    private TestableImsEnablementTracker createTracker(IImsServiceController binder, int state) {
+        TestableImsEnablementTracker tracker = new TestableImsEnablementTracker(
+                Looper.getMainLooper(), binder, state);
+        return tracker;
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 1a94727..23de7df 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -21,6 +21,20 @@
 import static android.net.NetworkStats.SET_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
+import static android.telephony.CarrierConfigManager.ImsVoice.ALERTING_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.BASIC_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.MIDCALL_SRVCC_SUPPORT;
+import static android.telephony.CarrierConfigManager.ImsVoice.PREALERTING_SRVCC_SUPPORT;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_INCOMING_SETUP;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_FAILED;
+import static android.telephony.TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+import static android.telephony.ims.ImsStreamMediaProfile.DIRECTION_INACTIVE;
+import static android.telephony.ims.ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
 
 import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
 
@@ -58,6 +72,7 @@
 import android.content.pm.PackageManager;
 import android.net.NetworkStats;
 import android.net.NetworkStats.Entry;
+import android.net.Uri;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.os.Bundle;
 import android.os.Message;
@@ -76,6 +91,8 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -91,6 +108,7 @@
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
+import com.android.ims.internal.ConferenceParticipant;
 import com.android.ims.internal.IImsCallSession;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
@@ -98,6 +116,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SrvccConnection;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.d2d.RtpTransport;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker.VtDataUsageProvider;
@@ -112,6 +131,8 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
 
@@ -125,6 +146,7 @@
     private ImsCall.Listener mImsCallListener;
     private ImsCall mImsCall;
     private ImsCall mSecondImsCall;
+    private ISrvccStartedCallback mSrvccStartedCallback;
     private BroadcastReceiver mBroadcastReceiver;
     private Bundle mBundle = new Bundle();
     private static final int SUB_0 = 0;
@@ -1689,7 +1711,7 @@
         assertTrue(mCTUT.mForegroundCall.isRingbackTonePlaying());
 
         // Move the connection to the handover state.
-        mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
 
         assertFalse(mCTUT.mForegroundCall.isRingbackTonePlaying());
     }
@@ -1720,7 +1742,7 @@
         }
 
         // Move the connection to the handover state.
-        mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
         // Ensure we are no longer tracking hold.
         assertFalse(mCTUT.isHoldOrSwapInProgress());
     }
@@ -1743,7 +1765,7 @@
         connection.addListener(mImsPhoneConnectionListener);
 
         // Move the connection to the handover state.
-        mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
         assertEquals(1, mCTUT.mHandoverCall.getConnections().size());
 
         // No need to go through all the rigamarole of the mocked termination we normally do; we
@@ -1781,7 +1803,7 @@
         connection.addListener(mImsPhoneConnectionListener);
 
         // Move the connection to the handover state.
-        mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
         assertEquals(1, mCTUT.mHandoverCall.getConnections().size());
 
         // Make sure the tracker states it's idle.
@@ -1892,7 +1914,7 @@
         startOutgoingCall();
 
         // Move the connection to the handover state.
-        mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
 
         try {
             // When trigger CallSessionUpdated after Srvcc completes, checking no exception.
@@ -1902,6 +1924,398 @@
         }
     }
 
+    /**
+     * Tests the case where a dialed call has not yet moved beyond the "pending MO" phase, but the
+     * user then disconnects.  In such a case we need to ensure that the pending MO reference is
+     * cleared so that another call can be placed.
+     */
+    @Test
+    @SmallTest
+    public void testCallDisconnectBeforeActive() {
+        ImsPhoneConnection connection = placeCall();
+        assertEquals(1, mCTUT.getConnections().size());
+        // Call is the pending MO right now.
+        assertEquals(connection, mCTUT.getPendingMO());
+        assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
+        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
+
+        mImsCallListener.onCallTerminated(connection.getImsCall(),
+                new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0));
+        // Make sure pending MO got nulled out.
+        assertNull(mCTUT.getPendingMO());
+
+        // Try making another call; it should not fail.
+        ImsPhoneConnection connection2 = placeCall();
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoNotSupported() throws Exception {
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+        ImsPhoneConnection heldMT = getImsPhoneConnection(Call.State.HOLDING, "5678", true);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(activeMO);
+        connections.add(heldMT);
+
+        ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+        ImsCallProfile heldProfile = getImsCallProfileForSrvccSync("heldCall", heldMT, false);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+
+        // active call
+        SrvccCall srvccProfile = new SrvccCall(
+                "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {});
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoBasicSrvcc() throws Exception {
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+        ImsPhoneConnection heldMT = getImsPhoneConnection(Call.State.HOLDING, "5678", true);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(activeMO);
+        connections.add(heldMT);
+
+        ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+        ImsCallProfile heldProfile = getImsCallProfileForSrvccSync("heldCall", heldMT, false);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        // active call
+        SrvccCall srvccProfile = new SrvccCall(
+                "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+        assertEquals("1234", srvccConnections[0].getNumber());
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoMoAlerting() throws Exception {
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection alertingMO = getImsPhoneConnection(Call.State.ALERTING, "1234", false);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(alertingMO);
+
+        ImsCallProfile alertingProfile = getImsCallProfileForSrvccSync("alertingCall", null, true);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        // alerting call, with local ringback tone
+        SrvccCall srvccProfile = new SrvccCall(
+                "alertingCall", PRECISE_CALL_STATE_ALERTING, alertingProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+
+        bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        ALERTING_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.ALERTING);
+        assertTrue(srvccConnections[0].getRingbackToneType() == SrvccConnection.TONE_LOCAL);
+
+        profiles.clear();
+
+        // alerting call, with network ringback tone
+        alertingProfile = getImsCallProfileForSrvccSync("alertingCall", null, false);
+
+        srvccProfile = new SrvccCall(
+                "alertingCall", PRECISE_CALL_STATE_ALERTING, alertingProfile);
+        profiles.add(srvccProfile);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.ALERTING);
+        assertTrue(srvccConnections[0].getRingbackToneType() == SrvccConnection.TONE_NETWORK);
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoMtAlerting() throws Exception {
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection alertingMT = getImsPhoneConnection(Call.State.INCOMING, "1234", false);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(alertingMT);
+
+        ImsCallProfile incomingProfile =
+                getImsCallProfileForSrvccSync("incomingCall", alertingMT, false);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        SrvccCall srvccProfile = new SrvccCall(
+                "incomingCall", PRECISE_CALL_STATE_INCOMING, incomingProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+
+        bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        ALERTING_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.INCOMING);
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoMtPreAlerting() throws Exception {
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        ImsCallProfile incomingProfile = getImsCallProfileForSrvccSync("incomingCall", null, false);
+
+        SrvccCall srvccProfile = new SrvccCall(
+                "incomingCallSetup", PRECISE_CALL_STATE_INCOMING_SETUP, incomingProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        ALERTING_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+
+        bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        ALERTING_SRVCC_SUPPORT,
+                        PREALERTING_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.INCOMING);
+        assertTrue(srvccConnections[0].getSubState() == SrvccConnection.SUBSTATE_PREALERTING);
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifySrvccStateStarted() throws Exception {
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        mSrvccStartedCallback = null;
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                mSrvccStartedCallback = (ISrvccStartedCallback) invocation.getArguments()[0];
+                return null;
+            }
+        }).when(mImsManager).notifySrvccStarted(any(ISrvccStartedCallback.class));
+
+        verify(mImsManager, times(0)).notifySrvccStarted(any());
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_STARTED);
+        verify(mImsManager, times(1)).notifySrvccStarted(any());
+        assertNotNull(mSrvccStartedCallback);
+
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(activeMO);
+
+        ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall", activeMO, false);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        // active call
+        SrvccCall srvccProfile = new SrvccCall(
+                "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+        profiles.add(srvccProfile);
+
+        mSrvccStartedCallback.onSrvccCallNotified(profiles);
+        SrvccConnection[] srvccConnections = mSimulatedCommands.getSrvccConnections();
+
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+        assertEquals("1234", srvccConnections[0].getNumber());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifySrvccStateFailed() throws Exception {
+        verify(mImsManager, times(0)).notifySrvccFailed();
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_FAILED);
+        verify(mImsManager, times(1)).notifySrvccFailed();
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifySrvccStateCanceled() throws Exception {
+        verify(mImsManager, times(0)).notifySrvccCanceled();
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_CANCELED);
+        verify(mImsManager, times(1)).notifySrvccCanceled();
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifySrvccStateCompleted() throws Exception {
+        verify(mImsManager, times(0)).notifySrvccCompleted();
+        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
+        verify(mImsManager, times(1)).notifySrvccCompleted();
+    }
+
+    @Test
+    @SmallTest
+    public void testConvertToSrvccConnectionInfoConferenceCall() throws Exception {
+        // setup ImsPhoneCallTracker's mConnections
+        ImsPhoneConnection activeMO = getImsPhoneConnection(Call.State.ACTIVE, "1234", false);
+
+        ArrayList<ImsPhoneConnection> connections = new ArrayList<ImsPhoneConnection>();
+        replaceInstance(ImsPhoneCallTracker.class, "mConnections", mCTUT, connections);
+        connections.add(activeMO);
+
+        List<ConferenceParticipant> participants = new ArrayList<ConferenceParticipant>();
+        participants.add(new ConferenceParticipant(Uri.parse("tel:1234"), "", null,
+                  android.telecom.Connection.STATE_ACTIVE,
+                  android.telecom.Call.Details.DIRECTION_INCOMING));
+        participants.add(new ConferenceParticipant(Uri.parse("tel:5678"), "", null,
+                  android.telecom.Connection.STATE_ACTIVE,
+                  android.telecom.Call.Details.DIRECTION_OUTGOING));
+
+        ImsCallProfile activeProfile = getImsCallProfileForSrvccSync("activeCall",
+                activeMO, false, participants);
+
+        // setup the response of notifySrvccStarted
+        List<SrvccCall> profiles = new ArrayList<>();
+
+        SrvccConnection[] srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNull(srvccConnections);
+
+        // active call
+        SrvccCall srvccProfile = new SrvccCall(
+                "activeCall", PRECISE_CALL_STATE_ACTIVE, activeProfile);
+        profiles.add(srvccProfile);
+
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 1);
+        assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+        assertFalse(srvccConnections[0].isMultiParty());
+        assertEquals("1234", srvccConnections[0].getNumber());
+
+        bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.ImsVoice.KEY_SRVCC_TYPE_INT_ARRAY,
+                new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        MIDCALL_SRVCC_SUPPORT
+                });
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        srvccConnections = mCTUT.convertToSrvccConnectionInfo(profiles);
+        assertNotNull(srvccConnections);
+        assertTrue(srvccConnections.length == 2);
+
+        assertTrue(srvccConnections[0].getState() == Call.State.ACTIVE);
+        assertTrue(srvccConnections[0].isMultiParty());
+        assertTrue(srvccConnections[0].isIncoming());
+        assertEquals("1234", srvccConnections[0].getNumber());
+
+        assertTrue(srvccConnections[1].getState() == Call.State.ACTIVE);
+        assertTrue(srvccConnections[1].isMultiParty());
+        assertFalse(srvccConnections[1].isIncoming());
+        assertEquals("5678", srvccConnections[1].getNumber());
+    }
+
     private void sendCarrierConfigChanged() {
         Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
@@ -1977,5 +2391,45 @@
         }
         return connection;
     }
+
+    private ImsPhoneConnection getImsPhoneConnection(Call.State state,
+            String number, boolean isIncoming) {
+        ImsPhoneCall call = mock(ImsPhoneCall.class);
+        doReturn(state).when(call).getState();
+
+        ImsPhoneConnection c = mock(ImsPhoneConnection.class);
+        doReturn(state).when(c).getState();
+        doReturn(isIncoming).when(c).isIncoming();
+        doReturn(call).when(c).getCall();
+        doReturn(number).when(c).getAddress();
+
+        return c;
+    }
+
+    private ImsCallProfile getImsCallProfileForSrvccSync(String callId,
+            ImsPhoneConnection c, boolean localTone) {
+        return getImsCallProfileForSrvccSync(callId, c, localTone, null);
+    }
+
+    private ImsCallProfile getImsCallProfileForSrvccSync(String callId,
+            ImsPhoneConnection c, boolean localTone, List<ConferenceParticipant> participants) {
+        ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile(0,
+                localTone ? DIRECTION_INACTIVE : DIRECTION_SEND_RECEIVE, 0, 0, 0);
+        ImsCallProfile profile = new ImsCallProfile(0, 0, null, mediaProfile);
+
+        if (c != null) {
+            ImsCallSession session = mock(ImsCallSession.class);
+            doReturn(callId).when(session).getCallId();
+
+            ImsCall imsCall = mock(ImsCall.class);
+            doReturn(profile).when(imsCall).getCallProfile();
+            doReturn(session).when(imsCall).getCallSession();
+            doReturn(participants).when(imsCall).getConferenceParticipants();
+
+            doReturn(imsCall).when(c).getImsCall();
+        }
+
+        return profile;
+    }
 }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
index 23b6bb9..2defe3c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
@@ -18,6 +18,7 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -26,9 +27,11 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.SsDomainController;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -117,4 +120,33 @@
                 .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, true);
         doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
     }
+
+    @Test
+    @SmallTest
+    public void testGetSuppServiceRoutingInfo() {
+        // Tests for valid service codes are done in SsDomainControllerTest.
+
+        // verifies that null returns when invalid service code is given
+        // emergency number
+        SsDomainController.SuppServiceRoutingInfo ssInfo =
+                ImsPhoneMmiCode.getSuppServiceRoutingInfo("911",
+                        (SsDomainController) null);
+        assertNull(ssInfo);
+
+        // normal number
+        ssInfo = ImsPhoneMmiCode.getSuppServiceRoutingInfo("0123456789",
+                (SsDomainController) null);
+        assertNull(ssInfo);
+        ssInfo = ImsPhoneMmiCode.getSuppServiceRoutingInfo("+1234567890",
+                (SsDomainController) null);
+        assertNull(ssInfo);
+
+        // USSD
+        ssInfo = ImsPhoneMmiCode.getSuppServiceRoutingInfo("*0123456789#",
+                (SsDomainController) null);
+        assertNull(ssInfo);
+        ssInfo = ImsPhoneMmiCode.getSuppServiceRoutingInfo("*1234#56789#",
+                (SsDomainController) null);
+        assertNull(ssInfo);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index a4e2574..5886422 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SHORT_CODE_SMS;
 import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
 import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -43,6 +44,7 @@
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -412,4 +414,45 @@
         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
         // TODO(b/153196254): verify atom contents
     }
-}
+
+    @Test
+    public void onPullAtom_outgoingShortCodeSms_empty() {
+        doReturn(new OutgoingShortCodeSms[0]).when(mPersistAtomsStorage)
+                .getOutgoingShortCodeSms(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_outgoingShortCodeSms_tooFrequent() {
+        doReturn(null).when(mPersistAtomsStorage).getOutgoingShortCodeSms(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+        verify(mPersistAtomsStorage, times(1))
+                .getOutgoingShortCodeSms(eq(MIN_COOLDOWN_MILLIS));
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onPullAtom_outgoingShortCodeSms_multipleSms() {
+        OutgoingShortCodeSms outgoingShortCodeSms = new OutgoingShortCodeSms();
+        doReturn(new OutgoingShortCodeSms[] {outgoingShortCodeSms, outgoingShortCodeSms,
+                outgoingShortCodeSms, outgoingShortCodeSms})
+                .when(mPersistAtomsStorage)
+                .getOutgoingShortCodeSms(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(4);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index e46b822..be2257b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -68,6 +69,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
@@ -224,6 +226,10 @@
     private SipTransportSession mSipTransportSession2;
     private SipTransportSession[] mSipTransportSession;
 
+    private OutgoingShortCodeSms mOutgoingShortCodeSms1;
+    private OutgoingShortCodeSms mOutgoingShortCodeSms2;
+    private OutgoingShortCodeSms[] mOutgoingShortCodeSms;
+
     private void makeTestData() {
         // MO call with SRVCC (LTE to UMTS)
         mCall1Proto = new VoiceCallSession();
@@ -859,6 +865,19 @@
 
         mSipTransportSession =
                 new SipTransportSession[] {mSipTransportSession1, mSipTransportSession2};
+
+        mOutgoingShortCodeSms1 = new OutgoingShortCodeSms();
+        mOutgoingShortCodeSms1.category = 1;
+        mOutgoingShortCodeSms1.xmlVersion = 30;
+        mOutgoingShortCodeSms1.shortCodeSmsCount = 1;
+
+        mOutgoingShortCodeSms2 = new OutgoingShortCodeSms();
+        mOutgoingShortCodeSms2.category = 2;
+        mOutgoingShortCodeSms2.xmlVersion  = 31;
+        mOutgoingShortCodeSms2.shortCodeSmsCount = 1;
+
+        mOutgoingShortCodeSms = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1,
+                mOutgoingShortCodeSms2};
     }
 
     private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
@@ -997,6 +1016,9 @@
         mSipTransportSession1 = null;
         mSipTransportSession2 = null;
         mSipTransportSession = null;
+        mOutgoingShortCodeSms1 = null;
+        mOutgoingShortCodeSms2 = null;
+        mOutgoingShortCodeSms = null;
         super.tearDown();
     }
 
@@ -3399,6 +3421,121 @@
     }
 
     @Test
+    public void addOutgoingShortCodeSms_emptyProto() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // OutgoingShortCodeSms should be added successfully, changes should be saved.
+        verifyCurrentStateSavedToFileOnce();
+        OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1};
+        assertProtoArrayEquals(expectedList,
+                mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+    }
+
+    @Test
+    public void addOutgoingShortCodeSms_withExistingEntries() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+        mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // OutgoingShortCodeSms should be added successfully.
+        verifyCurrentStateSavedToFileOnce();
+        OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1,
+                mOutgoingShortCodeSms2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList,
+                mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+    }
+
+    @Test
+    public void addOutgoingShortCodeSms_updateExistingEntries() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        // Add copy of mOutgoingShortCodeSms1.
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addOutgoingShortCodeSms(copyOf(mOutgoingShortCodeSms1));
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // mOutgoingShortCodeSms1's short code sms count should be increased by 1.
+        verifyCurrentStateSavedToFileOnce();
+        OutgoingShortCodeSms newOutgoingShortCodeSms1 = copyOf(mOutgoingShortCodeSms1);
+        newOutgoingShortCodeSms1.shortCodeSmsCount = 2;
+        OutgoingShortCodeSms[] expectedList = new OutgoingShortCodeSms[] {newOutgoingShortCodeSms1,
+                mOutgoingShortCodeSms2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList,
+                mPersistAtomsStorage.getOutgoingShortCodeSms(0L));
+    }
+
+    @Test
+    public void addOutgoingShortCodeSms_tooManyEntries() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Store mOutgoingShortCodeSms1 11 times.
+        for (int i = 0; i < 11; i++) {
+            mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms1);
+            mPersistAtomsStorage.incTimeMillis(100L);
+        }
+        // Store mOutgoingShortCodeSms2 1 time.
+        mPersistAtomsStorage.addOutgoingShortCodeSms(mOutgoingShortCodeSms2);
+
+        verifyCurrentStateSavedToFileOnce();
+        OutgoingShortCodeSms[] result = mPersistAtomsStorage
+                .getOutgoingShortCodeSms(0L);
+        assertHasStatsAndCount(result, mOutgoingShortCodeSms1, 11);
+        assertHasStatsAndCount(result, mOutgoingShortCodeSms2, 1);
+    }
+
+    @Test
+    public void getOutgoingShortCodeSms_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        // Pull interval less than minimum.
+        mPersistAtomsStorage.incTimeMillis(50L);
+        OutgoingShortCodeSms[] outgoingShortCodeSmsList = mPersistAtomsStorage
+                .getOutgoingShortCodeSms(100L);
+        // Should be denied.
+        assertNull(outgoingShortCodeSmsList);
+    }
+
+    @Test
+    public void getOutgoingShortCodeSms_withSavedAtoms() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        OutgoingShortCodeSms[] outgoingShortCodeSmsList1 = mPersistAtomsStorage
+                .getOutgoingShortCodeSms(50L);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        OutgoingShortCodeSms[] outgoingShortCodeSmsList2 = mPersistAtomsStorage
+                .getOutgoingShortCodeSms(50L);
+
+        // First set of results should be equal to file contents.
+        OutgoingShortCodeSms[] expectedOutgoingShortCodeSmsList =
+                new OutgoingShortCodeSms[] {mOutgoingShortCodeSms1, mOutgoingShortCodeSms2};
+        assertProtoArrayEqualsIgnoringOrder(expectedOutgoingShortCodeSmsList,
+                outgoingShortCodeSmsList1);
+        // Second set of results should be empty.
+        assertProtoArrayEquals(new OutgoingShortCodeSms[0], outgoingShortCodeSmsList2);
+        // Corresponding pull timestamp should be updated and saved.
+        assertEquals(START_TIME_MILLIS + 200L, mPersistAtomsStorage
+                .getAtomsProto().outgoingShortCodeSmsPullTimestampMillis);
+        InOrder inOrder = inOrder(mTestFileOutputStream);
+        assertEquals(START_TIME_MILLIS + 100L,
+                getAtomsWritten(inOrder).outgoingShortCodeSmsPullTimestampMillis);
+        assertEquals(START_TIME_MILLIS + 200L,
+                getAtomsWritten(inOrder).outgoingShortCodeSmsPullTimestampMillis);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
     @SmallTest
     public void clearAtoms() throws Exception {
         createTestFile(START_TIME_MILLIS);
@@ -3469,6 +3606,8 @@
         atoms.sipMessageResponse = mSipMessageResponse;
         atoms.sipTransportSessionPullTimestampMillis = lastPullTimeMillis;
         atoms.sipTransportSession = mSipTransportSession;
+        atoms.outgoingShortCodeSms = mOutgoingShortCodeSms;
+        atoms.outgoingShortCodeSmsPullTimestampMillis = lastPullTimeMillis;
         FileOutputStream stream = new FileOutputStream(mTestFile);
         stream.write(PersistAtoms.toByteArray(atoms));
         stream.close();
@@ -3587,6 +3726,11 @@
         return SipTransportSession.parseFrom(MessageNano.toByteArray(source));
     }
 
+    private static OutgoingShortCodeSms copyOf(OutgoingShortCodeSms source)
+            throws Exception {
+        return OutgoingShortCodeSms.parseFrom(MessageNano.toByteArray(source));
+    }
+
     private void assertAllPullTimestampEquals(long timestamp) {
         assertEquals(
                 timestamp,
@@ -3866,4 +4010,18 @@
         }
         assertEquals(expectedCount, actualCount);
     }
+
+    private static void assertHasStatsAndCount(
+            OutgoingShortCodeSms[] outgoingShortCodeSmsList,
+            @Nullable OutgoingShortCodeSms expectedOutgoingShortCodeSms, int expectedCount) {
+        assertNotNull(outgoingShortCodeSmsList);
+        int actualCount = -1;
+        for (OutgoingShortCodeSms outgoingShortCodeSms : outgoingShortCodeSmsList) {
+            if (outgoingShortCodeSms.category == expectedOutgoingShortCodeSms.category
+                    && outgoingShortCodeSms.xmlVersion == expectedOutgoingShortCodeSms.xmlVersion) {
+                actualCount = outgoingShortCodeSms.shortCodeSmsCount;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 111e5d2..c26dd22 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -25,11 +25,6 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
 import static com.android.internal.telephony.data.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
-import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -89,6 +84,11 @@
 import java.util.Arrays;
 
 public class TelephonyMetricsTest extends TelephonyTest {
+    private static final String FAKE_ADDRESS = "99.88.77.66";
+    private static final String FAKE_DNS = "55.66.77.88";
+    private static final String FAKE_GATEWAY = "11.22.33.44";
+    private static final String FAKE_IFNAME = "FAKE IFNAME";
+    private static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
     // Mocked classes
     private ImsCallSession mImsCallSession;
     private ServiceState mServiceState;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index a1a7866..8f509e2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -77,7 +77,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -895,7 +894,6 @@
 
     @Test
     @SmallTest
-    @Ignore("b/256234604")
     public void singleImsCall_ratSwitchToUnknown() {
         setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
         doReturn(true).when(mImsConnection0).isIncoming();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
index 2ac0c98..580d533 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
@@ -910,7 +910,8 @@
             suggestedTimes.set(timeSuggestion);
             if (timeSuggestion.getUnixEpochTime() != null) {
                 // The fake time service just uses the latest suggestion.
-                mFakeDeviceState.currentTimeMillis = timeSuggestion.getUnixEpochTime().getValue();
+                mFakeDeviceState.currentTimeMillis =
+                        timeSuggestion.getUnixEpochTime().getUnixEpochTimeMillis();
             }
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
new file mode 100644
index 0000000..f4db542
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -0,0 +1,892 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.subscription;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Looper;
+import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.ims.ImsMmTelManager;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SubscriptionDatabaseManagerTest extends TelephonyTest {
+    private static final String FAKE_ICCID1 = "123456";
+    private static final String FAKE_ICCID2 = "456789";
+    private static final String FAKE_PHONE_NUMBER1 = "6502530000";
+    private static final String FAKE_PHONE_NUMBER2 = "4089961010";
+    private static final String FAKE_CARRIER_NAME1 = "A-Mobile";
+    private static final String FAKE_CARRIER_NAME2 = "B-Mobile";
+    private static final int FAKE_COLOR1 = 1;
+    private static final int FAKE_COLOR2 = 3;
+    private static final int FAKE_CARRIER_ID1 = 1234;
+    private static final int FAKE_CARRIER_ID2 = 5678;
+    private static final String FAKE_COUNTRY_CODE1 = "TW";
+    private static final String FAKE_COUNTRY_CODE2 = "US";
+    private static final String FAKE_MCC1 = "466";
+    private static final String FAKE_MCC2 = "310";
+    private static final String FAKE_MNC1 = "01";
+    private static final String FAKE_MNC2 = "410";
+    private static final String FAKE_EHPLMNS1 = "46602,46603";
+    private static final String FAKE_EHPLMNS2 = "310411,310412";
+    private static final String FAKE_HPLMNS1 = "46601,46604";
+    private static final String FAKE_HPLMNS2 = "310410,310413";
+    private static final byte[] FAKE_NATIVE_ACCESS_RULES1 = UiccAccessRule.encodeRules(
+            new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package1", 12345L)});
+    private static final byte[] FAKE_NATIVE_ACCESS_RULES2 = UiccAccessRule.encodeRules(
+            new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package2", 45678L)});
+    private static final byte[] FAKE_CARRIER_CONFIG_ACCESS_RULES1 = UiccAccessRule.encodeRules(
+            new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package1", 54321L)});
+    private static final byte[] FAKE_CARRIER_CONFIG_ACCESS_RULES2 = UiccAccessRule.encodeRules(
+            new UiccAccessRule[]{new UiccAccessRule(new byte[] {}, "package2", 84954L)});
+    private static final String FAKE_UUID1 = "a684e31a-5998-4670-abdd-0561252c58a5";
+    private static final String FAKE_UUID2 = "cf6d7a9d-e712-4b3c-a600-7a2d4961b5b9";
+    private static final String FAKE_OWNER1 = "owner1";
+    private static final String FAKE_OWNER2 = "owner2";
+    private static final String FAKE_MOBILE_DATA_POLICY1 = "1,2";
+    private static final String FAKE_MOBILE_DATA_POLICY2 = "1";
+    private static final String FAKE_IMSI1 = "1234";
+    private static final String FAKE_IMSI2 = "5678";
+    private static final byte[] FAKE_RCS_CONFIG1 = new byte[]{0x01, 0x02, 0x03};
+    private static final byte[] FAKE_RCS_CONFIG2 = new byte[]{0x04, 0x05, 0x06};
+    private static final String FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1 = "carrier=123456, power=3";
+    private static final String FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2 = "user=1256, enable_2g=3";
+    private static final String FAKE_CONTACT1 = "John Smith, Tesla Forrest";
+    private static final String FAKE_CONTACT2 = "Mary Jane, Teresa Mill";
+    private static final int FAKE_TP_MESSAGE_REFERENCE1 = 123;
+    private static final int FAKE_TP_MESSAGE_REFERENCE2 = 456;
+    private static final int FAKE_USER_ID1 = 10;
+    private static final int FAKE_USER_ID2 = 11;
+
+    private static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO1 =
+            new SubscriptionInfoInternal.Builder()
+                    .setIccId(FAKE_ICCID1)
+                    .setSimSlotIndex(0)
+                    .setDisplayName(FAKE_CARRIER_NAME1)
+                    .setCarrierName(FAKE_CARRIER_NAME1)
+                    .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_SIM_SPN)
+                    .setIconTint(FAKE_COLOR1)
+                    .setNumber(FAKE_PHONE_NUMBER1)
+                    .setDataRoaming(SubscriptionManager.DATA_ROAMING_ENABLE)
+                    .setMcc(FAKE_MCC1)
+                    .setMnc(FAKE_MNC1)
+                    .setEhplmns(FAKE_EHPLMNS1)
+                    .setHplmns(FAKE_HPLMNS1)
+                    .setEmbedded(1)
+                    .setCardString(FAKE_ICCID1)
+                    .setCardId(1)
+                    .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES1)
+                    .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES1)
+                    .setRemovableEmbedded(0)
+                    .setEnhanced4GModeEnabled(1)
+                    .setVideoTelephonyEnabled(1)
+                    .setWifiCallingEnabled(1)
+                    .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED)
+                    .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
+                    .setWifiCallingModeForRoaming(1)
+                    .setOpportunistic(0)
+                    .setGroupUuid(FAKE_UUID1)
+                    .setCountryIso(FAKE_COUNTRY_CODE1)
+                    .setCarrierId(FAKE_CARRIER_ID1)
+                    .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL)
+                    .setType(SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)
+                    .setGroupOwner(FAKE_OWNER1)
+                    .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY1)
+                    .setImsi(FAKE_IMSI1)
+                    .setUiccApplicationsEnabled(1)
+                    .setRcsUceEnabled(1)
+                    .setCrossSimCallingEnabled(1)
+                    .setRcsConfig(FAKE_RCS_CONFIG1)
+                    .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS1)
+                    .setDeviceToDeviceStatusSharingPreference(
+                            SubscriptionManager.D2D_SHARING_ALL_CONTACTS)
+                    .setVoImsOptInEnabled(1)
+                    .setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT1)
+                    .setNrAdvancedCallingEnabled(1)
+                    .setNumberFromCarrier(FAKE_PHONE_NUMBER1)
+                    .setNumberFromIms(FAKE_PHONE_NUMBER1)
+                    .setPortIndex(0)
+                    .setUsageSetting(SubscriptionManager.USAGE_SETTING_DEFAULT)
+                    .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE1)
+                    .setUserId(FAKE_USER_ID1)
+                    .setGroupDisabled(false)
+                    .build();
+
+    private static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO2 =
+            new SubscriptionInfoInternal.Builder()
+                    .setIccId(FAKE_ICCID2)
+                    .setSimSlotIndex(1)
+                    .setDisplayName(FAKE_CARRIER_NAME2)
+                    .setCarrierName(FAKE_CARRIER_NAME2)
+                    .setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER)
+                    .setIconTint(FAKE_COLOR2)
+                    .setNumber(FAKE_PHONE_NUMBER2)
+                    .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE)
+                    .setMcc(FAKE_MCC2)
+                    .setMnc(FAKE_MNC2)
+                    .setEhplmns(FAKE_EHPLMNS2)
+                    .setHplmns(FAKE_HPLMNS2)
+                    .setEmbedded(0)
+                    .setCardString(FAKE_ICCID2)
+                    .setCardId(2)
+                    .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES2)
+                    .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES2)
+                    .setRemovableEmbedded(1)
+                    .setEnhanced4GModeEnabled(0)
+                    .setVideoTelephonyEnabled(0)
+                    .setWifiCallingEnabled(0)
+                    .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
+                    .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED)
+                    .setWifiCallingModeForRoaming(0)
+                    .setOpportunistic(1)
+                    .setGroupUuid(FAKE_UUID2)
+                    .setCountryIso(FAKE_COUNTRY_CODE2)
+                    .setCarrierId(FAKE_CARRIER_ID2)
+                    .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING)
+                    .setType(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM)
+                    .setGroupOwner(FAKE_OWNER2)
+                    .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY2)
+                    .setImsi(FAKE_IMSI2)
+                    .setUiccApplicationsEnabled(0)
+                    .setRcsUceEnabled(0)
+                    .setCrossSimCallingEnabled(0)
+                    .setRcsConfig(FAKE_RCS_CONFIG2)
+                    .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2)
+                    .setDeviceToDeviceStatusSharingPreference(
+                            SubscriptionManager.D2D_SHARING_SELECTED_CONTACTS)
+                    .setVoImsOptInEnabled(0)
+                    .setDeviceToDeviceStatusSharingContacts(FAKE_CONTACT2)
+                    .setNrAdvancedCallingEnabled(0)
+                    .setNumberFromCarrier(FAKE_PHONE_NUMBER2)
+                    .setNumberFromIms(FAKE_PHONE_NUMBER2)
+                    .setPortIndex(1)
+                    .setUsageSetting(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC)
+                    .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE2)
+                    .setUserId(FAKE_USER_ID2)
+                    .setGroupDisabled(false)
+                    .build();
+
+    private SubscriptionDatabaseManager mDatabaseManagerUT;
+
+    private final SubscriptionProvider mSubscriptionProvider = new SubscriptionProvider();
+
+    private static class SubscriptionProvider extends MockContentProvider {
+        private final List<ContentValues> mDatabase = new ArrayList<>();
+
+        private final List<String> mAllColumns;
+
+        SubscriptionProvider() {
+            mAllColumns = Telephony.SimInfo.getAllColumns();
+        }
+
+        @Override
+        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+                String sortOrder) {
+            logd("SubscriptionProvider: query. uri=" + uri);
+            if (!Telephony.SimInfo.CONTENT_URI.equals(uri)) {
+                throw new UnsupportedOperationException("Unsupported uri=" + uri);
+            }
+            if (projection != null || selection != null || selectionArgs != null) {
+                throw new UnsupportedOperationException("Only support full database query. "
+                        + "projection=" + Arrays.toString(projection) + ", selection=" + selection
+                        + ", selectionArgs=" + Arrays.toString(selectionArgs));
+            }
+
+            MatrixCursor mc = new MatrixCursor(mAllColumns.toArray(new String[0]));
+
+            // Only support full database query
+            for (int row = 0; row < mDatabase.size(); row++) {
+                List<Object> values = new ArrayList<>();
+                for (String column : mAllColumns) {
+                    values.add(mDatabase.get(row).get(column));
+                }
+                mc.addRow(values);
+            }
+
+            return mc;
+        }
+
+        @Override
+        public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
+            if (!uri.isPathPrefixMatch(Telephony.SimInfo.CONTENT_URI)) {
+                throw new UnsupportedOperationException("Unsupported uri=" + uri);
+            }
+
+            int subId = Integer.parseInt(uri.getLastPathSegment());
+            assertThat(mDatabase.size()).isAtLeast(subId);
+
+            ContentValues existingValues = mDatabase.get(subId - 1);
+            logd("update: subId=" + subId + ", contentValues=" + values);
+            for (Map.Entry<String, Object> entry : values.valueSet()) {
+                String column = entry.getKey();
+                Object value = entry.getValue();
+                if (!mAllColumns.contains(column)) {
+                    throw new IllegalArgumentException("Update with unknown column " + column);
+                }
+                existingValues.putObject(column, value);
+            }
+            return 1;
+        }
+
+        @Override
+        public int delete(Uri uri, String selection, String[] selectionArgs) {
+            throw new UnsupportedOperationException("delete is not supported uri=" + uri);
+        }
+
+        @Override
+        public Uri insert(Uri uri, ContentValues values) {
+            logd("SubscriptionProvider: insert. uri=" + uri + ", values=" + values);
+            if (!Telephony.SimInfo.CONTENT_URI.equals(uri)) {
+                throw new UnsupportedOperationException("Unsupported uri=" + uri);
+            }
+
+            for (String column : values.keySet()) {
+                if (!mAllColumns.contains(column)) {
+                    throw new IllegalArgumentException("Insert with unknown column " + column);
+                }
+            }
+            int subId = mDatabase.size() + 1;
+            values.put(Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, subId);
+            mDatabase.add(values);
+            return ContentUris.withAppendedId(Telephony.SimInfo.CONTENT_URI, subId);
+        }
+    }
+
+    private void loadFromDatabase() throws Exception {
+        Method method = SubscriptionDatabaseManager.class.getDeclaredMethod("loadFromDatabase");
+        method.setAccessible(true);
+        method.invoke(mDatabaseManagerUT);
+        processAllMessages();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("SubscriptionDatabaseManagerTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        ((MockContentResolver) mContext.getContentResolver()).addProvider(
+                Telephony.Carriers.CONTENT_URI.getAuthority(), mSubscriptionProvider);
+        doReturn(1).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID1));
+        doReturn(2).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID2));
+        mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper());
+        logd("SubscriptionDatabaseManagerTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Verify the subscription from the cache and database is same as the provided one.
+     *
+     * @param subInfo The subscription to compare.
+     */
+    private void verifySubscription(@NonNull SubscriptionInfoInternal subInfo) throws Exception {
+        int subId = subInfo.getSubscriptionId();
+        // Verify the cache value is same as the inserted one.
+        assertWithMessage("Subscription info cache value is different.")
+                .that(mDatabaseManagerUT.getSubscriptionInfoInternal(subId)).isEqualTo(subInfo);
+
+        // Load subscription info from the database.
+        loadFromDatabase();
+        // Verify the database value is same as the inserted one.
+        assertWithMessage("Subscription info database value is different.")
+                .that(mDatabaseManagerUT.getSubscriptionInfoInternal(subId)).isEqualTo(subInfo);
+    }
+
+    /**
+     * Insert a subscription info into the database and verify it's in the cache and database.
+     *
+     * @param subInfo The subscription info to insert.
+     * @return The inserted subscription info.
+     */
+    private SubscriptionInfoInternal insertSubscriptionAndVerify(
+            @NonNull SubscriptionInfoInternal subInfo) throws Exception {
+        assertThat(subInfo.getSubscriptionId()).isEqualTo(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        int subId = mDatabaseManagerUT.insertSubscriptionInfo(subInfo);
+        assertThat(SubscriptionManager.isValidSubscriptionId(subId)).isTrue();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setId(subId).build();
+        verifySubscription(subInfo);
+        return subInfo;
+    }
+
+    @Test
+    public void testGetAllColumns() throws Exception {
+        Field[] declaredFields = Telephony.SimInfo.class.getDeclaredFields();
+        List<String> columnNames = new ArrayList<>();
+        for (Field field : declaredFields) {
+            if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("COLUMN_")) {
+                columnNames.add((String) field.get(null));
+            }
+        }
+        // When you add a new column in Telephony.SimInfo, did you remember to modify
+        // Telephony.SimInfo.getAllColumns() as well?
+        assertThat(Telephony.SimInfo.getAllColumns()).containsExactlyElementsIn(columnNames);
+    }
+
+    @Test
+    public void testInsertSubscription() throws Exception {
+        assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1).getSubscriptionId())
+                .isEqualTo(1);
+        assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2).getSubscriptionId())
+                .isEqualTo(2);
+    }
+
+    @Test
+    public void testUpdateSubscription() throws Exception {
+        assertThat(insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1).getSubscriptionId())
+                .isEqualTo(1);
+        SubscriptionInfoInternal subInfo = new SubscriptionInfoInternal
+                .Builder(FAKE_SUBSCRIPTION_INFO2)
+                .setId(1)
+                .build();
+        mDatabaseManagerUT.updateSubscription(subInfo);
+        processAllMessages();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateIccId() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setIccId(subInfo.getSubscriptionId(), FAKE_ICCID2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setIccId(FAKE_ICCID2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateSimSlotIndex() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setSimSlotIndex(subInfo.getSubscriptionId(),
+                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setSimSlotIndex(
+                SubscriptionManager.INVALID_SIM_SLOT_INDEX).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateDisplayName() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setDisplayName(subInfo.getSubscriptionId(), FAKE_CARRIER_NAME2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setDisplayName(
+                FAKE_CARRIER_NAME2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCarrierName() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCarrierName(subInfo.getSubscriptionId(), FAKE_CARRIER_NAME2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setCarrierName(
+                FAKE_CARRIER_NAME2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateDisplayNameSource() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCarrierName(subInfo.getSubscriptionId(), FAKE_CARRIER_NAME2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setCarrierName(
+                FAKE_CARRIER_NAME2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateIconTint() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setIconTint(subInfo.getSubscriptionId(), FAKE_COLOR2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setIconTint(FAKE_COLOR2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateNumber() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setNumber(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setNumber(FAKE_PHONE_NUMBER2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateDataRoaming() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setDataRoaming(subInfo.getSubscriptionId(),
+                SubscriptionManager.DATA_ROAMING_DISABLE);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setDataRoaming(SubscriptionManager.DATA_ROAMING_DISABLE).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateMcc() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setMcc(subInfo.getSubscriptionId(), FAKE_MCC2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setMcc(FAKE_MCC2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateMnc() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setMnc(subInfo.getSubscriptionId(), FAKE_MNC2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setMnc(FAKE_MNC2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateEhplmns() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setEhplmns(subInfo.getSubscriptionId(), FAKE_EHPLMNS2.split(","));
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEhplmns(FAKE_EHPLMNS2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateHplmns() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setHplmns(subInfo.getSubscriptionId(), FAKE_HPLMNS2.split(","));
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setHplmns(FAKE_HPLMNS2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateEmbedded() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setEmbedded(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEmbedded(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCardString() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCardString(subInfo.getSubscriptionId(), FAKE_ICCID2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setCardString(FAKE_ICCID2)
+                .setCardId(2)
+                .build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateNativeAccessRules() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setNativeAccessRules(subInfo.getSubscriptionId(),
+                UiccAccessRule.decodeRules(FAKE_NATIVE_ACCESS_RULES2));
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setNativeAccessRules(FAKE_NATIVE_ACCESS_RULES2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCarrierConfigAccessRules() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCarrierConfigAccessRules(subInfo.getSubscriptionId(),
+                UiccAccessRule.decodeRules(FAKE_CARRIER_CONFIG_ACCESS_RULES2));
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setCarrierConfigAccessRules(FAKE_CARRIER_CONFIG_ACCESS_RULES2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateRemovableEmbedded() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setRemovableEmbedded(subInfo.getSubscriptionId(), true);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setRemovableEmbedded(1).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateEnhanced4GModeEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setEnhanced4GModeEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setEnhanced4GModeEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateVideoTelephonyEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setVideoTelephonyEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setVideoTelephonyEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateWifiCallingEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setWifiCallingEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setWifiCallingEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateWifiCallingMode() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setWifiCallingMode(subInfo.getSubscriptionId(),
+                ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setWifiCallingMode(ImsMmTelManager.WIFI_MODE_WIFI_ONLY).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateWifiCallingModeForRoaming() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setWifiCallingModeForRoaming(subInfo.getSubscriptionId(),
+                ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setWifiCallingModeForRoaming(ImsMmTelManager.WIFI_MODE_WIFI_ONLY).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateWifiCallingEnabledForRoaming() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setWifiCallingEnabledForRoaming(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setWifiCallingEnabledForRoaming(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateOpportunistic() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setOpportunistic(subInfo.getSubscriptionId(), true);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setOpportunistic(1).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateGroupUuid() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setGroupUuid(subInfo.getSubscriptionId(), FAKE_UUID2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo).setGroupUuid(FAKE_UUID2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCountryIso() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCountryIso(subInfo.getSubscriptionId(), FAKE_COUNTRY_CODE2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setCountryIso(FAKE_COUNTRY_CODE2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCarrierId() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCarrierId(subInfo.getSubscriptionId(), FAKE_CARRIER_ID2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setCarrierId(FAKE_CARRIER_ID2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateProfileClass() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setProfileClass(subInfo.getSubscriptionId(),
+                SubscriptionManager.PROFILE_CLASS_TESTING);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_TESTING).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateSubscriptionType() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setSubscriptionType(subInfo.getSubscriptionId(),
+                SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setType(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateGroupOwner() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setGroupOwner(subInfo.getSubscriptionId(), FAKE_OWNER2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setGroupOwner(FAKE_OWNER2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateEnabledMobileDataPolicies() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setEnabledMobileDataPolicies(subInfo.getSubscriptionId(),
+                FAKE_MOBILE_DATA_POLICY2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setEnabledMobileDataPolicies(FAKE_MOBILE_DATA_POLICY2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateImsi() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setImsi(subInfo.getSubscriptionId(), FAKE_IMSI2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setImsi(FAKE_IMSI2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateUiccApplicationsEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setUiccApplicationsEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setUiccApplicationsEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateRcsUceEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setRcsUceEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setRcsUceEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateCrossSimCallingEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setCrossSimCallingEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setCrossSimCallingEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateRcsConfig() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setRcsConfig(subInfo.getSubscriptionId(), FAKE_RCS_CONFIG2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setRcsConfig(FAKE_RCS_CONFIG2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateAllowedNetworkTypesForReasons() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setAllowedNetworkTypesForReasons(subInfo.getSubscriptionId(),
+                FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setAllowedNetworkTypesForReasons(FAKE_ALLOWED_NETWORK_TYPES_FOR_REASONS2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateDeviceToDeviceStatusSharingPreference() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setDeviceToDeviceStatusSharingPreference(subInfo.getSubscriptionId(),
+                SubscriptionManager.D2D_SHARING_DISABLED);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setDeviceToDeviceStatusSharingPreference(
+                        SubscriptionManager.D2D_SHARING_DISABLED).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateNrAdvancedCallingEnabled() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setNrAdvancedCallingEnabled(subInfo.getSubscriptionId(), false);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setNrAdvancedCallingEnabled(0).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateNumberFromCarrier() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setNumberFromCarrier(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setNumberFromCarrier(FAKE_PHONE_NUMBER2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateNumberFromIms() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setNumberFromIms(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setNumberFromIms(FAKE_PHONE_NUMBER2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdatePortIndex() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setPortIndex(subInfo.getSubscriptionId(), 1);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setPortIndex(1).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateUsageSetting() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setUsageSetting(subInfo.getSubscriptionId(),
+                SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setUsageSetting(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateLastUsedTPMessageReference() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setLastUsedTPMessageReference(subInfo.getSubscriptionId(),
+                FAKE_TP_MESSAGE_REFERENCE2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setLastUsedTPMessageReference(FAKE_TP_MESSAGE_REFERENCE2).build();
+        verifySubscription(subInfo);
+    }
+
+    @Test
+    public void testUpdateUserId() throws Exception {
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setUserId(subInfo.getSubscriptionId(), FAKE_USER_ID2);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setUserId(FAKE_USER_ID2).build();
+        verifySubscription(subInfo);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java
new file mode 100644
index 0000000..c040b9e
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/AdnRecordCacheTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static com.android.internal.telephony.uicc.IccConstants.EF_ADN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_MBDN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_PBR;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.gsm.UsimPhoneBookManager;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class AdnRecordCacheTest extends TelephonyTest {
+
+    private AdnRecordCacheUT mAdnRecordCache;
+    private TestLooper mTestLooper;
+    private Handler mTestHandler;
+    private IccFileHandler mFhMock;
+    private UsimPhoneBookManager mUsimPhoneBookManager;
+
+    @SuppressWarnings("ClassCanBeStatic")
+    private class AdnRecordCacheUT extends AdnRecordCache {
+        AdnRecordCacheUT(IccFileHandler fh, UsimPhoneBookManager usimPhoneBookManager) {
+            super(fh, usimPhoneBookManager);
+        }
+
+        protected void setAdnLikeWriters(int key, ArrayList<Message> waiters) {
+            super.setAdnLikeWriters(key, waiters);
+        }
+
+        protected void setAdnLikeFiles(int key, ArrayList<AdnRecord> adnRecordList) {
+            super.setAdnLikeFiles(key, adnRecordList);
+        }
+
+        protected void setUserWriteResponse(int key, Message message) {
+            super.setUserWriteResponse(key, message);
+        }
+
+        protected UsimPhoneBookManager getUsimPhoneBookManager() {
+            return super.getUsimPhoneBookManager();
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        // Mocked classes
+        mFhMock = mock(IccFileHandler.class);
+        mUsimPhoneBookManager = mock(UsimPhoneBookManager.class);
+        mTestLooper = new TestLooper();
+        mTestHandler = new Handler(mTestLooper.getLooper());
+        mTestHandler.post(
+                () -> mAdnRecordCache = new AdnRecordCacheUT(mFhMock, mUsimPhoneBookManager));
+        mTestLooper.dispatchAll();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTestLooper != null) {
+            mTestLooper.dispatchAll();
+            mTestLooper = null;
+        }
+        mTestHandler.removeCallbacksAndMessages(null);
+        mTestHandler = null;
+        mAdnRecordCache = null;
+        super.tearDown();
+    }
+
+    @Test
+    public void resetTest() {
+        Message message1 = Message.obtain(mTestHandler);
+        Message message2 = Message.obtain(mTestHandler);
+
+        // test data to create mAdnLikeWaiters
+        ArrayList<Message> waiters = new ArrayList<>();
+        waiters.add(message1);
+        mAdnRecordCache.setAdnLikeWriters(EF_MBDN, waiters);
+
+        // test data to create mAdnLikeFiles
+        setAdnLikeFiles(EF_MBDN);
+
+        // test data to create mUserWriteResponse
+        mAdnRecordCache.setUserWriteResponse(EF_MBDN, message2);
+
+        mAdnRecordCache.reset();
+
+        mTestLooper.dispatchAll();
+        AsyncResult ar1 = (AsyncResult) message1.obj;
+        AsyncResult ar2 = (AsyncResult) message2.obj;
+        Assert.assertTrue(ar1.exception.toString().contains("AdnCache reset"));
+        Assert.assertTrue(ar2.exception.toString().contains("AdnCace reset"));
+    }
+
+    @Test
+    public void updateAdnByIndexEfException() {
+        int efId = 0x6FC5;
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.updateAdnByIndex(efId, null, 0, null, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains("EF is not known ADN-like EF:0x6FC5")));
+    }
+
+    @Test
+    public void updateAdnByIndex_WriteResponseException() {
+        int efId = EF_MBDN;
+        Message message = Message.obtain(mTestHandler);
+        Message message2 = Message.obtain(mTestHandler);
+        AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+        // test data to create mUserWriteResponse
+        mAdnRecordCache.setUserWriteResponse(efId, message2);
+        mAdnRecordCache.updateAdnByIndex(efId, adnRecord, 0, null, message);
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains("Have pending update for EF:0x6FC7")));
+    }
+
+    @Test
+    public void updateAdnByIndex() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, "success2", null);
+                    response.sendToTarget();
+                    return response;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        Assert.assertNotNull(message);
+        AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+        // test data to create mUserWriteResponse
+        mAdnRecordCache.updateAdnByIndex(EF_MBDN, adnRecord, 0, null, message);
+        mTestLooper.startAutoDispatch();
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void updateAdnBySearch_EfException() {
+        int efId = 0x6FC5;
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.updateAdnBySearch(efId, null, null, null, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains("EF is not known ADN-like EF:0x6FC5")));
+    }
+
+    @Test
+    public void updateAdnBySearch_Exception() {
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.updateAdnBySearch(EF_MBDN, null, null, null, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains("Adn list not exist for EF:0x6FC7")));
+    }
+
+    @Test
+    public void updateAdnBySearch_AdnListError() {
+        int efId = EF_MBDN;
+        setAdnLikeFiles(efId);
+        Message message = Message.obtain(mTestHandler);
+        AdnRecord oldAdn = new AdnRecord("oldAlphaTag", "123456789");
+        mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains(
+                "Adn record don't exist for ADN Record 'oldAlphaTag'")));
+    }
+
+    @Test
+    public void updateAdnBySearch_PendingUpdate() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, "success2", null);
+                    response.sendToTarget();
+                    return response;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        int efId = EF_MBDN;
+        setAdnLikeFiles(efId);
+        Message message = Message.obtain(mTestHandler);
+        AdnRecord oldAdn = new AdnRecord("AlphaTag", "123456789");
+        mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+        mTestLooper.dispatchAll();
+
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void updateAdnBySearch() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, "success", null);
+                    response.sendToTarget();
+                    return response;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        int efId = EF_MBDN;
+        setAdnLikeFiles(efId);
+        Message message = Message.obtain(mTestHandler);
+        AdnRecord oldAdn = new AdnRecord("AlphaTag", "123456789");
+        mAdnRecordCache.updateAdnBySearch(efId, oldAdn, null, null, message);
+        mTestLooper.dispatchAll();
+
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+    }
+
+
+    @Test
+    public void updateAdnBySearch_AdnException() {
+        doReturn(null).when(mUsimPhoneBookManager).loadEfFilesFromUsim();
+        Message message = Message.obtain(mTestHandler);
+        AdnRecord oldAdn = new AdnRecord("oldAlphaTag", "123456789");
+        mAdnRecordCache.updateAdnBySearch(EF_PBR, oldAdn, null, null, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNotNull(ar.exception);
+        assertTrue((ar.exception.toString().contains("Adn list not exist for EF:0x4F30")));
+    }
+
+    @Test
+    public void requestLoadAllAdnLike_AlreadyLoadedEf() {
+        int efId = EF_MBDN;
+        setAdnLikeFiles(efId);
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.requestLoadAllAdnLike(efId, 0, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNull(ar.exception);
+        Assert.assertNotNull(ar.result);
+    }
+
+    @Test
+    public void requestLoadAllAdnLike_AlreadyLoadingEf() {
+        int efId = EF_MBDN;
+        // test data to create mAdnLikeWaiters
+        Message message = Message.obtain(mTestHandler);
+        ArrayList<Message> waiters = new ArrayList<>();
+        waiters.add(message);
+        mAdnRecordCache.setAdnLikeWriters(efId, waiters);
+        mAdnRecordCache.requestLoadAllAdnLike(efId, 0, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertNull(ar);
+    }
+
+    @Test
+    public void requestLoadAllAdnLike_NotKnownEf() {
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.requestLoadAllAdnLike(EF_MBDN, -1, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        Assert.assertTrue(ar.exception.toString().contains("EF is not known ADN-like EF:0x"));
+    }
+
+    @Test
+    public void requestLoadAllAdnLike() {
+                doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, null, new CommandException(
+                            CommandException.Error.REQUEST_NOT_SUPPORTED));
+                    response.sendToTarget();
+                    return response;
+                })
+                .when(mFhMock)
+                .loadEFLinearFixedAll(anyInt(), anyString(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mAdnRecordCache.requestLoadAllAdnLike(EF_ADN, 0x6FC8, message);
+        mTestLooper.dispatchAll();
+
+        verify(mFhMock, times(1)).loadEFLinearFixedAll(anyInt(), anyString(), any(Message.class));
+    }
+
+    private void setAdnLikeFiles(int ef) {
+        // test data to create mAdnLikeFiles
+        ArrayList<AdnRecord> adnRecordList = new ArrayList<>();
+        AdnRecord adnRecord = new AdnRecord("AlphaTag", "123456789");
+        adnRecordList.add(adnRecord);
+        mAdnRecordCache.setAdnLikeFiles(ef, adnRecordList);
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java
new file mode 100644
index 0000000..63e68e2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccFileHandlerTest.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+import android.util.Log;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+
+public class IccFileHandlerTest {
+    CommandsInterface mCi;
+    IccFileHandler mIccFileHandler;
+    private TestLooper mTestLooper;
+    private Handler mTestHandler;
+
+    @Before
+    public void setUp() throws Exception {
+        mCi = mock(CommandsInterface.class);
+        mTestLooper = new TestLooper();
+        mTestHandler = new Handler(mTestLooper.getLooper());
+        mTestHandler.post(
+                () -> mIccFileHandler = new IccFileHandler(mCi) {
+                    @Override
+                    protected String getEFPath(int efid) {
+                        switch (efid) {
+                            case 0x4f30:
+                            case 0x4f3a:
+                                return "3F007F105F3A";
+                        }
+                        return "";
+                    }
+
+                    @Override
+                    protected void logd(String s) {
+                        Log.d("IccFileHandlerTest", s);
+                    }
+
+                    @Override
+                    protected void loge(String s) {
+                        Log.d("IccFileHandlerTest", s);
+                    }
+                });
+
+
+        mTestLooper.dispatchAll();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTestLooper != null) {
+            mTestLooper.dispatchAll();
+            mTestLooper = null;
+        }
+        mTestHandler.removeCallbacksAndMessages(null);
+        mTestHandler = null;
+        mIccFileHandler = null;
+        mCi = null;
+    }
+
+    @Test
+    public void loadEFLinearFixed_WithNullPath() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixed(0, "", 0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFLinearFixed() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixed(0, 0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFImgLinearFixed() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFImgLinearFixed(0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void getEFLinearRecordSize_WithNullPath() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.getEFLinearRecordSize(0, "", message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void getEFLinearRecordSize() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.getEFLinearRecordSize(0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void getEFTransparentRecordSize() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.getEFTransparentRecordSize(0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_FileNotFoundAtGetRecord() throws InterruptedException {
+        int efId = 0x4f30;
+        final CountDownLatch latch = new CountDownLatch(1);
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    IccIoResult iir = new IccIoResult(0x94, 0x00,
+                            IccUtils.hexStringToBytes(null));
+                    AsyncResult.forMessage(response, iir, null);
+                    mTestHandler.postDelayed(latch::countDown, 100);
+                    response.sendToTarget();
+                    return null;
+                }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+                anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(efId, null, message);
+        mTestLooper.startAutoDispatch();
+        latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertNotNull(ar);
+        assertTrue(ar.exception instanceof IccFileNotFound);
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_FileNotFoundAtReadRecord() throws InterruptedException {
+        int efid = 0x4f30;
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    IccIoResult iir = null;
+                    if (response.what == 6) {
+                        iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(
+                                "000000454F30040000FFFF01020145"));
+                        latch.countDown();
+                    } else if (response.what == 7) {
+                        iir = new IccIoResult(0x94, 0x00, IccUtils.hexStringToBytes(null));
+                        mTestHandler.postDelayed(latch::countDown, 100);
+                    }
+                    AsyncResult.forMessage(response, iir, null);
+                    response.sendToTarget();
+                    return null;
+                }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+                anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+        mTestLooper.startAutoDispatch();
+        latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertNotNull(ar);
+        assertTrue(ar.exception instanceof IccFileNotFound);
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_ExceptionAtGetRecord() throws InterruptedException {
+        int efid = 0x4f30;
+        final CountDownLatch latch = new CountDownLatch(1);
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, null, new CommandException(
+                            CommandException.Error.OPERATION_NOT_ALLOWED));
+                    response.sendToTarget();
+                    mTestHandler.postDelayed(latch::countDown, 100);
+                    return null;
+                }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+                anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+        mTestLooper.startAutoDispatch();
+        latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertSame(CommandException.Error.OPERATION_NOT_ALLOWED,
+                ((CommandException) ar.exception).getCommandError());
+        assertNull(ar.result);
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_ExceptionAtReadRecord() throws InterruptedException {
+        int efid = 0x4f30;
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    String hexString = null;
+                    IccIoResult iir = null;
+                    if (response.what == 6) {
+                        hexString = "000000454F30040000FFFF01020145";
+                        iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                        AsyncResult.forMessage(response, iir, null);
+                        latch.countDown();
+                    } else if (response.what == 7) {
+                        AsyncResult.forMessage(response, null, new CommandException(
+                                CommandException.Error.OPERATION_NOT_ALLOWED));
+                        mTestHandler.postDelayed(latch::countDown, 100);
+                    }
+                    response.sendToTarget();
+                    mTestHandler.postDelayed(latch::countDown, 100);
+                    return null;
+                }).when(mCi).iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(),
+                anyInt(),
+                isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+        mTestLooper.startAutoDispatch();
+        latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertSame(CommandException.Error.OPERATION_NOT_ALLOWED,
+                ((CommandException) ar.exception).getCommandError());
+        assertNull(ar.result);
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_FullLoop() throws InterruptedException {
+        int efid = 0x4f30;
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    String hexString = null;
+                    if (response.what == 6) {
+                        hexString = "000000454F30040000FFFF01020145";
+                        latch.countDown();
+                    } else if (response.what == 7) {
+                        try {
+                            IccFileHandler.LoadLinearFixedContext lc =
+                                    (IccFileHandler.LoadLinearFixedContext) response.obj;
+                            if (mIccFileHandler.getEfid(lc) == efid) {
+                                hexString =
+                                        "A814C0034F3A01C1034F3202C5034F0904C9034F2109A90FC3034F611"
+                                                + "5C4034F1108CA034F5017AA0FC2034F4A03C7034F4B06CB"
+                                                + "034F4F16FFFFFFFFFFFFFFFFFFFFFFFFFF";
+                            }
+                            mTestHandler.postDelayed(latch::countDown, 100);
+
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            Log.e("UsimFH", e.getMessage());
+                        }
+
+                    }
+                    IccIoResult iir = new IccIoResult(0x90, 0x00,
+                            IccUtils.hexStringToBytes(hexString));
+                    AsyncResult.forMessage(response, iir, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(efid, null, message);
+        mTestLooper.startAutoDispatch();
+        latch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertNotNull(ar);
+        ArrayList<byte[]> results = (ArrayList<byte[]>) ar.result;
+        assertEquals(
+                "A814C0034F3A01C1034F3202C5034F0904C9034F2109A90FC3034F6115C4034F1108CA03"
+                        + "4F5017AA0FC2034F4A03C7034F4B06CB034F4F16FFFFFFFFFFFFFFFFFFFFFFFFFF",
+                IccUtils.bytesToHexString(results.get(0)));
+        verify(mCi, times(2)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFLinearFixedAll_WithNullPath() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(0, "", message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFLinearFixedAll() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFLinearFixedAll(0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFTransparent() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFTransparent(0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFTransparent_WithZeroSize() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFTransparent(0, 0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void loadEFImgTransparent() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        isNull(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.loadEFImgTransparent(0, 0, 0, 0, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), isNull(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void updateEFLinearFixed_WithNullPath() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        anyString(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.updateEFLinearFixed(0, "", 0, new byte[10], null, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void updateEFLinearFixed() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        anyString(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.updateEFLinearFixed(0, 0, new byte[10], null, message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+    }
+
+    @Test
+    public void updateEFTransparent() {
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(9);
+                    AsyncResult.forMessage(response, "Success", null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mCi)
+                .iccIOForApp(anyInt(), anyInt(), anyString(), anyInt(), anyInt(), anyInt(),
+                        anyString(), isNull(), isNull(), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIccFileHandler.updateEFTransparent(0, new byte[10], message);
+        verify(mCi, times(1)).iccIOForApp(anyInt(), anyInt(), anyString(),
+                anyInt(), anyInt(), anyInt(), anyString(), isNull(), isNull(), any(Message.class));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java
new file mode 100644
index 0000000..b3e0a85
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccIoResultTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IccIoResultTest {
+
+    @Test
+    public void check0x90_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x90, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+    }
+
+    @Test
+    public void check0x91_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x91, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+    }
+
+    @Test
+    public void check0x9E_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x9E, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+    }
+
+    @Test
+    public void check0x9F_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x9F, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(resultStr != null && (!resultStr.contains("Error")));
+    }
+
+    @Test
+    public void check0x94_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x94, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(resultStr != null && (resultStr.contains("no EF selected")));
+
+        iccIoResult = new IccIoResult(0x94, 0x02, new byte[10]);
+        resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("out f range (invalid address)")));
+
+        iccIoResult = new IccIoResult(0x94, 0x04, new byte[10]);
+        resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("file ID not found/pattern not found")));
+
+        iccIoResult = new IccIoResult(0x94, 0x08, new byte[10]);
+        resultStr = iccIoResult.toString();
+
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("file is inconsistent with the command")));
+    }
+
+    @Test
+    public void check0x98_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x98, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x98, 0x02, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("no CHV initialized")));
+
+        iccIoResult = new IccIoResult(0x98, 0x04, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("access condition not fulfilled")));
+
+        iccIoResult = new IccIoResult(0x98, 0x08, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("in contradiction with CHV status")));
+
+        iccIoResult = new IccIoResult(0x98, 0x10, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "in contradiction with invalidation status")));
+
+        iccIoResult = new IccIoResult(0x98, 0x40, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "unsuccessful CHV verification, no attempt left")));
+
+        iccIoResult = new IccIoResult(0x98, 0x50, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "increase cannot be performed, Max value reached")));
+
+        iccIoResult = new IccIoResult(0x98, 0x62, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "authentication error, application specific")));
+
+        iccIoResult = new IccIoResult(0x98, 0x64, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "authentication error, security context not supported")));
+
+        iccIoResult = new IccIoResult(0x98, 0x65, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("key freshness failure")));
+
+        iccIoResult = new IccIoResult(0x98, 0x66, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "authentication error, no memory space available")));
+
+        iccIoResult = new IccIoResult(0x98, 0x67, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "authentication error, no memory space available in EF_MUK")));
+    }
+
+    @Test
+    public void check0x61_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x61, 0x20, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("more response bytes available")));
+    }
+
+    @Test
+    public void check0x62_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x62, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+        iccIoResult = new IccIoResult(0x62, 0x81, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains(
+                        "part of returned data may be corrupted")));
+
+        iccIoResult = new IccIoResult(0x62, 0x82, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "end of file/record reached before reading Le bytes")));
+
+        iccIoResult = new IccIoResult(0x62, 0x83, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("selected file invalidated")));
+
+        iccIoResult = new IccIoResult(0x62, 0x84, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("selected file in termination state")));
+
+        iccIoResult = new IccIoResult(0x62, 0xF1, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("more data available")));
+
+        iccIoResult = new IccIoResult(0x62, 0xF2, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "more data available and proactive command pending")));
+
+        iccIoResult = new IccIoResult(0x62, 0xF3, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("response data available")));
+
+        iccIoResult = new IccIoResult(0x62, 0xF4, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+    }
+
+    @Test
+    public void check0x63_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x63, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains(
+                        "command successful but after using an internal update retry routine 0 "
+                                + "times")));
+
+        iccIoResult = new IccIoResult(0x63, 0xF1, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("more data expected")));
+
+        iccIoResult = new IccIoResult(0x63, 0xF2, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains(
+                        "more data expected and proactive command pending")));
+
+        iccIoResult = new IccIoResult(0x63, 0xF3, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("unknown")));
+    }
+
+    @Test
+    public void check0x64_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x64, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x64, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("no information given")));
+    }
+
+    @Test
+    public void check0x65_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x65, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x65, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue((resultStr != null) && (resultStr.contains(
+                "no information given, state of non-volatile memory changed")));
+
+        iccIoResult = new IccIoResult(0x65, 0x81, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("memory problem")));
+    }
+
+    @Test
+    public void check0x67_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x67, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "the interpretation of this status word is command dependent")));
+
+        iccIoResult = new IccIoResult(0x67, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "incorrect parameter P3")));
+    }
+
+    @Test
+    public void check0x68_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x68, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x68, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+        iccIoResult = new IccIoResult(0x68, 0x81, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("logical channel not supported")));
+
+        iccIoResult = new IccIoResult(0x68, 0x82, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("secure messaging not supported")));
+    }
+
+    @Test
+    public void check0x69_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x69, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x69, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("no information given")));
+
+        iccIoResult = new IccIoResult(0x69, 0x81, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains(
+                        "command incompatible with file structure")));
+
+        iccIoResult = new IccIoResult(0x69, 0x82, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("security status not satisfied")));
+
+        iccIoResult = new IccIoResult(0x69, 0x83, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("authentication/PIN method blocked")));
+
+        iccIoResult = new IccIoResult(0x69, 0x84, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("referenced data invalidated")));
+
+        iccIoResult = new IccIoResult(0x69, 0x85, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("conditions of use not satisfied")));
+
+        iccIoResult = new IccIoResult(0x69, 0x86, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue((resultStr != null) && (resultStr.contains(
+                "command not allowed (no EF selected)")));
+
+        iccIoResult = new IccIoResult(0x69, 0x89, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue((resultStr != null) && (resultStr.contains(
+                "command not allowed - secure channel - security not satisfied")));
+    }
+
+    @Test
+    public void check0x6A_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6A, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("unknown")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x80, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "incorrect parameters in the data field")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x81, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("function not supported")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x82, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("file not found")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x83, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("record not found")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x84, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("not enough memory space")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x86, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                (resultStr != null) && (resultStr.contains("incorrect parameters P1 to P2")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x87, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue((resultStr != null) && (resultStr.contains(
+                "lc inconsistent with P1 to P2")));
+
+        iccIoResult = new IccIoResult(0x6A, 0x88, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue((resultStr != null) && (resultStr.contains(
+                "referenced data not found")));
+    }
+
+    @Test
+    public void check0x6B_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6B, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(
+                resultStr != null && (resultStr.contains("incorrect parameter P1 or P2")));
+    }
+
+    @Test
+    public void check0x6C_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6C, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains("wrong length, retry with ")));
+    }
+
+    @Test
+    public void check0x6D_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6D, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "unknown instruction code given in the command")));
+    }
+
+    @Test
+    public void check0x6E_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6E, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "wrong instruction class given in the command")));
+    }
+
+    @Test
+    public void check0x6F_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x6F, 0xC0, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "the interpretation of this status word is command dependent")));
+
+        iccIoResult = new IccIoResult(0x6F, 0x00, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "technical problem with no diagnostic given")));
+    }
+
+    @Test
+    public void check0x92_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x92, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "command successful but after using an internal update retry routine")));
+
+        iccIoResult = new IccIoResult(0x92, 0x40, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "memory problem")));
+
+        iccIoResult = new IccIoResult(0x92, 0x41, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "unknown")));
+    }
+
+    @Test
+    public void check0x93_ErrorCodeParsing() {
+        IccIoResult iccIoResult = new IccIoResult(0x93, 0x00, new byte[10]);
+        String resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "SIM Application Toolkit is busy. Command cannot be executed"
+                        + " at present, further normal commands are allowed")));
+
+        iccIoResult = new IccIoResult(0x93, 0x41, new byte[10]);
+        resultStr = iccIoResult.toString();
+        Assert.assertTrue(resultStr != null && (resultStr.contains(
+                "unknown")));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
index b97ae1f..6f4666c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
@@ -30,13 +30,23 @@
 package com.android.internal.telephony.uicc;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
-import android.os.HandlerThread;
+import android.os.Handler;
 import android.os.Message;
+import android.os.test.TestLooper;
 
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -46,31 +56,48 @@
 
 public class IsimUiccRecordsTest extends TelephonyTest {
 
-    private IsimUiccRecords mIsimUiccRecords;
+    // Mocked classes
+    private IccFileHandler mFhMock;
+    private TestLooper mTestLooper;
+    private Handler mTestHandler;
+    private IsimUiccRecordsUT mIsimUiccRecordsUT;
 
-    private class IsimUiccRecordsTestHandler extends HandlerThread {
-        private IsimUiccRecordsTestHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext, mSimulatedCommands);
-            setReady(true);
+    @SuppressWarnings("ClassCanBeStatic")
+    private class IsimUiccRecordsUT extends IsimUiccRecords {
+        IsimUiccRecordsUT(UiccCardApplication app, Context c,
+                CommandsInterface ci, IccFileHandler mFhMock) {
+            super(app, c, ci);
+            mFh = mFhMock;
         }
     }
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        new IsimUiccRecordsTestHandler(TAG).start();
-        waitUntilReady();
+        mFhMock = mock(IccFileHandler.class);
+        mTestLooper = new TestLooper();
+        mTestHandler = new Handler(mTestLooper.getLooper());
+        mTestHandler.post(
+                () -> {
+                    mIsimUiccRecordsUT =
+                            new IsimUiccRecordsUT(
+                                    mUiccCardApplication3gpp,
+                                    mContext,
+                                    mSimulatedCommands,
+                                    mFhMock);
+                });
+        mTestLooper.dispatchAll();
     }
 
     @After
     public void tearDown() throws Exception {
-        mIsimUiccRecords.dispose();
-        mIsimUiccRecords = null;
+        if (mTestLooper != null) {
+            mTestLooper.dispatchAll();
+            mTestLooper = null;
+        }
+        mTestHandler.removeCallbacksAndMessages(null);
+        mTestHandler = null;
+        mIsimUiccRecordsUT = null;
         super.tearDown();
     }
 
@@ -79,12 +106,274 @@
         Message msg = new Message();
         msg.what = IccRecords.EVENT_REFRESH;
         msg.obj = new AsyncResult(null, null, null);
-        mIsimUiccRecords.handleMessage(msg);
+        mIsimUiccRecordsUT.handleMessage(msg);
         ArgumentCaptor<Intent> intentCapture = ArgumentCaptor.forClass(Intent.class);
         verify(mContext).sendBroadcast(intentCapture.capture());
-
         assertEquals(
-            ((Intent) intentCapture.getValue()).getAction(), IsimUiccRecords.INTENT_ISIM_REFRESH);
+                ((Intent) intentCapture.getValue()).getAction(),
+                IsimUiccRecords.INTENT_ISIM_REFRESH);
     }
 
-}
+    @Test
+    public void testPsiSmscTelValue() {
+        // Testing smsc successfully reading case
+        String smscTest = "tel:+13123149810";
+        String hexSmsc =
+                "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+        byte[] smscBytes = getStringToByte(hexSmsc);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+        AsyncResult.forMessage(message, smscBytes, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(smscTest, mIsimUiccRecordsUT.getSmscIdentity());
+    }
+
+    private byte[] getStringToByte(String hexSmsc) {
+        byte[] smscBytes = IccUtils.hexStringToBytes(hexSmsc);
+        return smscBytes;
+    }
+
+    @Test
+    public void testGetPsiSmscSipValue() {
+        // Testing smsc successfully reading case
+        String smscTest = "sip:+12063130004@msg.pc.t-mobile.com;user=phone";
+        byte[] smscBytes = getStringToByte(
+                "802F7369703A2B3132303633313330303034406D73672E70632E742D6D6F62696C6"
+                        + "52E636F6D3B757365723D70686F6E65FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+        AsyncResult.forMessage(message, smscBytes, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(smscTest, mIsimUiccRecordsUT.getSmscIdentity());
+    }
+
+    @Test
+    public void testGetPsiSmscValueException() {
+        // Testing smsc exception handling case
+        String hexSmsc =
+                "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+        byte[] smscBytes = getStringToByte(hexSmsc);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+        AsyncResult.forMessage(message, smscBytes,
+                new CommandException(
+                        CommandException.Error.OPERATION_NOT_ALLOWED));
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(null, mIsimUiccRecordsUT.getSmscIdentity());
+    }
+
+    @Test
+    public void testGetPsiSmscValueInvalidObject() {
+        // Testing smsc invalid data handling case
+        String smscTest = "tel:+13123149810";
+        byte[] smscBytes = GsmAlphabet.stringToGsm8BitPacked(smscTest);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getPsiSmscObject());
+        AsyncResult.forMessage(message, smscBytes,
+                new CommandException(
+                        CommandException.Error.OPERATION_NOT_ALLOWED));
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(null, mIsimUiccRecordsUT.getSmscIdentity());
+    }
+
+    @Test
+    public void testGetSmssTpmrValue() {
+        // Testing tpmr successfully reading case
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) (tpmr & 0xFF);
+        IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+        AsyncResult.forMessage(message, null, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(tpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testGetSmssTpmrValueException() {
+        // Testing tpmr reading fail case [ exception case ]
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) (tpmr & 0xFF);
+        IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+        AsyncResult.forMessage(message, null,
+                new CommandException(
+                        CommandException.Error.OPERATION_NOT_ALLOWED));
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(-1, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testGetSmssTpmrValueExtreme() {
+        // Testing extreme tpmr value case [ fails ]
+        byte[] smss = new byte[2];
+        int tpmr = 400;
+        smss[0] = (byte) (tpmr & 0xFF);
+        IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+        AsyncResult.forMessage(message, null, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(144, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+    private void setValidSmssValue() {
+        // preset/ initialize smssvalue
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) (tpmr & 0xFF);
+        IccRecords.SmssRecord smssRecord = mIsimUiccRecordsUT.createSmssRecord(null, smss);
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+        AsyncResult.forMessage(message, null, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        assertEquals(tpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrValue() {
+        // Testing tpmr successfully setting case
+        setValidSmssValue();
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        assertEquals(updateTpmr, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+
+    @Test
+    public void testSetSmssTpmrValueException() {
+        // Testing exception while setting TPMR [ with out setting initial value]
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+        Message message = Message.obtain(mTestHandler);
+        mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        assertEquals(-1, mIsimUiccRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrValueException2() {
+        // Testing exception while setting TPMR
+        setValidSmssValue();
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+
+                    AsyncResult.forMessage(response, true, new CommandException(
+                            CommandException.Error.OPERATION_NOT_ALLOWED));
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+        Message message = Message.obtain(mTestHandler);
+        mIsimUiccRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertTrue(((CommandException) ar.exception).getCommandError() ==
+                CommandException.Error.OPERATION_NOT_ALLOWED);
+    }
+
+    @Test
+    public void testGetSimServiceTable() {
+        // reading sim service table successfully case
+        byte[] sst = new byte[9];
+        for (int i = 0; i < sst.length; i++) {
+            if (i % 2 == 0) {
+                sst[i] = 0;
+            } else {
+                sst[i] = 1;
+            }
+        }
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+        AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        String mockSst = IccUtils.bytesToHexString(sst);
+        String resultSst = mIsimUiccRecordsUT.getIsimIst();
+        assertEquals(mockSst, resultSst);
+    }
+
+    @Test
+    public void testGetSimServiceTableException() {
+        // sim service table exception handling case
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+        AsyncResult ar = AsyncResult.forMessage(message,  null, new CommandException(
+                CommandException.Error.OPERATION_NOT_ALLOWED));
+        mIsimUiccRecordsUT.handleMessage(message);
+        String resultSst = mIsimUiccRecordsUT.getIsimIst();
+        assertEquals(null, resultSst);
+    }
+
+    @Test
+    public void testGetSsimServiceTableLessTableSize() {
+        // The less IST table size will not give any problem
+        byte[] sst = new byte[5];
+        for (int i = 0; i < sst.length; i++) {
+            if (i % 2 == 0) {
+                sst[i] = 0;
+            } else {
+                sst[i] = 1;
+            }
+        }
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+        AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        String mockSst = IccUtils.bytesToHexString(sst);
+        String resultSst = mIsimUiccRecordsUT.getIsimIst();
+        assertEquals(mockSst, resultSst);
+    }
+
+    @Test
+    public void testGetSsimServiceTableLargeTableSize() {
+        // The Big IST table size will not give any problem [ in feature the table may grows]
+        byte[] sst = new byte[99];
+        for (int i = 0; i < sst.length; i++) {
+            if (i % 2 == 0) {
+                sst[i] = 0;
+            } else {
+                sst[i] = 1;
+            }
+        }
+        Message message = mIsimUiccRecordsUT.obtainMessage(
+                IccRecords.EVENT_GET_ICC_RECORD_DONE, mIsimUiccRecordsUT.getIsimIstObject());
+        AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+        mIsimUiccRecordsUT.handleMessage(message);
+        String mockSst = IccUtils.bytesToHexString(sst);
+        String resultSst = mIsimUiccRecordsUT.getIsimIst();
+        assertEquals(mockSst, resultSst);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
index 9183235..79cd6a4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
@@ -22,9 +22,11 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -41,15 +43,19 @@
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo;
 import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName;
+import com.android.telephony.Rlog;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 public class SIMRecordsTest extends TelephonyTest {
@@ -169,37 +175,37 @@
 
     private void setUpSetForbiddenPlmnsTests() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                AsyncResult.forMessage(response, EF_SIZE, null);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .getEFTransparentRecordSize(anyInt(), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    AsyncResult.forMessage(response, EF_SIZE, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .getEFTransparentRecordSize(anyInt(), any(Message.class));
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(2);
-                AsyncResult.forMessage(response, true, null);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
     }
 
     @Test
     public void testGetForbiddenPlmns() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                byte[] encodedFplmn = IccUtils.encodeFplmns(SHORT_FPLMNS_LIST, EF_SIZE);
-                AsyncResult.forMessage(response, encodedFplmn, null);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    byte[] encodedFplmn = IccUtils.encodeFplmns(SHORT_FPLMNS_LIST, EF_SIZE);
+                    AsyncResult.forMessage(response, encodedFplmn, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
 
         Message message = Message.obtain(mTestHandler);
         mSIMRecordsUT.getForbiddenPlmns(message);
@@ -214,15 +220,15 @@
     @Test
     public void testGetForbiddenPlmnsException() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                AsyncResult.forMessage(response, null, new CommandException(
-                        CommandException.Error.OPERATION_NOT_ALLOWED));
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    AsyncResult.forMessage(response, null, new CommandException(
+                            CommandException.Error.OPERATION_NOT_ALLOWED));
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
 
         Message message = Message.obtain(mTestHandler);
         mSIMRecordsUT.getForbiddenPlmns(message);
@@ -238,14 +244,14 @@
     @Test
     public void testGetForbiddenPlmnsNull() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                AsyncResult.forMessage(response);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    AsyncResult.forMessage(response);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
 
         Message message = Message.obtain(mTestHandler);
         mSIMRecordsUT.getForbiddenPlmns(message);
@@ -259,15 +265,15 @@
     @Test
     public void testGetForbiddenPlmnsEmptyList() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                byte[] encodedFplmn = IccUtils.encodeFplmns(EMPTY_FPLMN_LIST, EF_SIZE);
-                AsyncResult.forMessage(response, encodedFplmn, null);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    byte[] encodedFplmn = IccUtils.encodeFplmns(EMPTY_FPLMN_LIST, EF_SIZE);
+                    AsyncResult.forMessage(response, encodedFplmn, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
 
         Message message = Message.obtain(mTestHandler);
         mSIMRecordsUT.getForbiddenPlmns(message);
@@ -282,14 +288,14 @@
     @Test
     public void testGetForbiddenPlmnsInvalidLength() {
         doAnswer(
-            invocation -> {
-                Message response = invocation.getArgument(1);
-                AsyncResult.forMessage(response, new byte[] { (byte) 0xFF, (byte) 0xFF }, null);
-                response.sendToTarget();
-                return null;
-            })
-            .when(mFhMock)
-            .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+                invocation -> {
+                    Message response = invocation.getArgument(1);
+                    AsyncResult.forMessage(response, new byte[]{(byte) 0xFF, (byte) 0xFF}, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
 
         Message message = Message.obtain(mTestHandler);
         mSIMRecordsUT.getForbiddenPlmns(message);
@@ -313,7 +319,7 @@
         targetPnns.add(new PlmnNetworkName(name, null));
 
         Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_PNN_DONE);
-        AsyncResult ar = AsyncResult.forMessage(message, rawPnns, null);
+        AsyncResult.forMessage(message, rawPnns, null);
         mSIMRecordsUT.handleMessage(message);
         List<PlmnNetworkName> parsedPnns = Arrays.asList(mSIMRecordsUT.getPnns());
 
@@ -321,23 +327,215 @@
     }
 
     private static byte[] encodePnn(String name) {
-        byte[] gsm7BitName = new byte[] {};
+        byte[] gsm7BitName = new byte[]{};
         try {
             gsm7BitName = GsmAlphabet.stringToGsm7BitPacked(name);
             gsm7BitName[0] = (byte) (name.length() % 8 | 0x80);
         } catch (Exception ex) {
             fail("SimRecordsTest: GsmAlphabet.stringToGsm7BitPacked() exception:" + ex);
         }
-
         byte[] encodedName = new byte[gsm7BitName.length + 2];
         encodedName[0] = 0x43;
         encodedName[1] = (byte) gsm7BitName.length;
         System.arraycopy(gsm7BitName, 0, encodedName, 2, gsm7BitName.length);
-
         return encodedName;
     }
 
     @Test
+    public void testGetSmssTpmrValue() {
+        // Testing tpmr successfully reading case
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) tpmr;
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_SMSS_RECORD_DONE, smss);
+        AsyncResult.forMessage(message, smss, null);
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(tpmr, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testGetSmssTpmrValueException() {
+        // Testing tpmr exception case
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) tpmr;
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_SMSS_RECORD_DONE, smss);
+        AsyncResult.forMessage(message, smss, new CommandException(
+                CommandException.Error.OPERATION_NOT_ALLOWED));
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(-1, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrValue() {
+        // Testing tpmr successfully updating case
+        setValidSmssValue();
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        assertEquals(updateTpmr, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrNegativevalue() {
+        // Testing tpmr successfully updating case
+        setValidSmssValue();
+        int updateTpmr = -2;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        // 10 is previous set value
+        assertEquals(10, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrHighvalue() {
+        // Testing tpmr successfully updating case
+        setValidSmssValue();
+        int updateTpmr = 256;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        // 10 is previous set value
+        assertEquals(10, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrMaxValue() {
+        // Testing tpmr successfully updating case
+        setValidSmssValue();
+        int updateTpmr = 255;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        assertEquals(updateTpmr, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrValueException() {
+        // Testing exception while setting TPMR [ with out setting initial value]
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, null);
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+        assertEquals(-1, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
+    public void testSetSmssTpmrValueException2() {
+        // Testing exception while setting TPMR
+        setValidSmssValue();
+        int updateTpmr = 30;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+
+                    AsyncResult.forMessage(response, true, new CommandException(
+                            CommandException.Error.OPERATION_NOT_ALLOWED));
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof CommandException);
+        assertTrue(((CommandException) ar.exception).getCommandError() ==
+                CommandException.Error.OPERATION_NOT_ALLOWED);
+    }
+
+    @Test
+    public void testSetSmssTpmrLargeValue() {
+        // Testing Large TPMR value setting
+        setValidSmssValue();
+        int updateTpmr = 300;
+        doAnswer(
+                invocation -> {
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, true, new CommandException(
+                            CommandException.Error.OPERATION_NOT_ALLOWED));
+                    response.sendToTarget();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setSmssTpmrValue(updateTpmr, message);
+        mTestLooper.dispatchAll();
+
+        AsyncResult ar = (AsyncResult) message.obj;
+        assertTrue(ar.exception instanceof IllegalArgumentException);
+    }
+
+    private void setValidSmssValue() {
+        // preset/ initialize smssvalue
+        byte[] smss = new byte[2];
+        int tpmr = 10;
+        smss[0] = (byte) (tpmr & 0xFF);
+        IccRecords.SmssRecord smssRecord = mSIMRecordsUT.createSmssRecord(null, smss);
+        Message message = mSIMRecordsUT.obtainMessage(
+                IsimUiccRecords.EVENT_SET_SMSS_RECORD_DONE, smssRecord);
+        AsyncResult.forMessage(message, null, null);
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(tpmr, mSIMRecordsUT.getSmssTpmrValue());
+    }
+
+    @Test
     public void testGetEfOpl() {
         ArrayList<byte[]> rawOpl = new ArrayList<byte[]>();
         List<OperatorPlmnInfo> targetOpl = new ArrayList<OperatorPlmnInfo>();
@@ -352,7 +550,7 @@
         targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
 
         Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
-        AsyncResult ar = AsyncResult.forMessage(message, rawOpl, null);
+        AsyncResult.forMessage(message, rawOpl, null);
         mSIMRecordsUT.handleMessage(message);
         List<OperatorPlmnInfo> parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
 
@@ -368,7 +566,7 @@
         targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
 
         message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
-        ar = AsyncResult.forMessage(message, rawOpl, null);
+        AsyncResult.forMessage(message, rawOpl, null);
         mSIMRecordsUT.handleMessage(message);
         parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
 
@@ -383,7 +581,7 @@
         rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex));
 
         message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
-        ar = AsyncResult.forMessage(message, rawOpl, null);
+        AsyncResult.forMessage(message, rawOpl, null);
         mSIMRecordsUT.handleMessage(message);
         parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
 
@@ -405,7 +603,341 @@
         data[5] = (byte) (lacTacEnd >>> 8);
         data[6] = (byte) lacTacEnd;
         data[7] = (byte) pnnIndex;
-
         return data;
     }
+
+    @Test
+    public void testGetPsiSmscTelValue() {
+        // Testing smsc successfully reading case
+        String smscTest = "tel:+13123149810";
+        String hexSmsc =
+                "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+        byte[] smscBytes = getStringToByte(hexSmsc);
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+        AsyncResult.forMessage(message, smscBytes, null);
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(smscTest, mSIMRecordsUT.getSmscIdentity());
+    }
+
+    private byte[] getStringToByte(String hexSmsc) {
+        byte[] smscBytes = IccUtils.hexStringToBytes(hexSmsc);
+        return smscBytes;
+    }
+
+    @Test
+    public void testGetPsiSmscSipValue() {
+        // Testing smsc successfully reading case
+        String smscTest = "sip:+12063130004@msg.pc.t-mobile.com;user=phone";
+        byte[] smscBytes = getStringToByte(
+                "802F7369703A2B3132303633313330303034406D73672E70632E742D6D6F62696C6"
+                        + "52E636F6D3B757365723D70686F6E65FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+        AsyncResult.forMessage(message, smscBytes, null);
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(smscTest, mSIMRecordsUT.getSmscIdentity());
+    }
+
+    @Test
+    public void testGetPsiSmscException() {
+        // Testing smsc exception handling case
+        String hexSmsc =
+                "801074656C3A2B3133313233313439383130FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                        + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
+        byte[] smscBytes = getStringToByte(hexSmsc);
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+        AsyncResult ar = AsyncResult.forMessage(message, smscBytes,
+                new CommandException(
+                        CommandException.Error.OPERATION_NOT_ALLOWED));
+        mSIMRecordsUT.handleMessage(message);
+        assertTrue(ar.exception instanceof CommandException);
+        assertEquals(null, mSIMRecordsUT.getSmscIdentity());
+    }
+
+    @Test
+    public void testGetPsiSmscValueInvalidObject() {
+        // Testing smsc invalid data handling case
+        String smscTest = "tel:+13123149810";
+        byte[] smscBytes = GsmAlphabet.stringToGsm8BitPacked(smscTest);
+        Message message = mSIMRecordsUT.obtainMessage(
+                SIMRecords.EVENT_GET_PSISMSC_DONE, smscBytes);
+        AsyncResult ar = AsyncResult.forMessage(message, smscBytes,
+                new CommandException(
+                        CommandException.Error.OPERATION_NOT_ALLOWED));
+        mSIMRecordsUT.handleMessage(message);
+        assertEquals(null, mSIMRecordsUT.getSmscIdentity());
+        assertTrue(ar.exception instanceof CommandException);
+    }
+
+    @Test
+    public void testGetSimServiceTable() {
+        // reading sim service table successfully case
+        byte[] sst = new byte[111];
+        for (int i = 0; i < sst.length; i++) {
+            if (i % 2 == 0) {
+                sst[i] = 0;
+            } else {
+                sst[i] = 1;
+            }
+        }
+        Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+        AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+        mSIMRecordsUT.handleMessage(message);
+        String mockSst = IccUtils.bytesToHexString(sst);
+        String resultSst = mSIMRecordsUT.getSimServiceTable();
+        assertEquals(mockSst, resultSst);
+    }
+
+    @Test
+    public void testGetSimServiceTableException() {
+        // sim service table exception handling case
+        Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+        AsyncResult ar = AsyncResult.forMessage(message, null, new CommandException(
+                CommandException.Error.OPERATION_NOT_ALLOWED));
+        mSIMRecordsUT.handleMessage(message);
+        String resultSst = mSIMRecordsUT.getSimServiceTable();
+        assertEquals(null, resultSst);
+    }
+
+    @Test
+    public void testGetSsimServiceTableLessTableSize() {
+        // sim service table reading case
+        byte[] sst = new byte[12];
+        for (int i = 0; i < sst.length; i++) {
+            if (i % 2 == 0) {
+                sst[i] = 0;
+            } else {
+                sst[i] = 1;
+            }
+        }
+        Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_SST_DONE);
+        AsyncResult ar = AsyncResult.forMessage(message, sst, null);
+        mSIMRecordsUT.handleMessage(message);
+        String mockSst = IccUtils.bytesToHexString(sst);
+        String resultSst = mSIMRecordsUT.getSimServiceTable();
+        assertEquals(mockSst, resultSst);
+    }
+
+    @Test
+    @Ignore("b/256282468")
+    public void testSetVoiceMailNumber() throws InterruptedException {
+
+        String voiceMailNumber = "1234567890";
+        String alphaTag = "Voicemail";
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation1");
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation2");
+                    Message response = invocation.getArgument(5);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+                        eq(null), any(Message.class));
+
+
+        mSIMRecordsUT.setMailboxIndex(1);
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+        latch.await(5, TimeUnit.SECONDS);
+        mTestLooper.startAutoDispatch();
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+        verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null),
+                anyInt(), any(byte[].class), eq(null), any(Message.class));
+
+        assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+        assertEquals(alphaTag, mSIMRecordsUT.getVoiceMailAlphaTag());
+    }
+
+    @Test
+    @Ignore("b/256282468")
+    public void testSetVoiceMailNumberBigAlphatag() throws InterruptedException {
+
+        String voiceMailNumber = "1234567890";
+        String alphaTag = "VoicemailAlphaTag-VoicemailAlphaTag";
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation1");
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation2");
+                    Message response = invocation.getArgument(5);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+                        eq(null), any(Message.class));
+
+
+        mSIMRecordsUT.setMailboxIndex(1);
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+        latch.await(8, TimeUnit.SECONDS);
+        mTestLooper.startAutoDispatch();
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+        verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null),
+                anyInt(), any(byte[].class), eq(null), any(Message.class));
+
+        //if attempt to save bugAlphatag which sim don't support so we will make it null
+        assertEquals(null, mSIMRecordsUT.getVoiceMailAlphaTag());
+        assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+    }
+
+    @Test
+    @Ignore("b/256282468")
+    public void testSetVoiceMailNumberUtf16Alphatag() throws InterruptedException {
+
+        String voiceMailNumber = "1234567890";
+        String alphaTag = "หมายเลขข้อความเสียง"; // Messagerie vocale
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation1");
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation2");
+                    Message response = invocation.getArgument(5);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+                        eq(null), any(Message.class));
+
+
+        mSIMRecordsUT.setMailboxIndex(1);
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+        latch.await(5, TimeUnit.SECONDS);
+        mTestLooper.startAutoDispatch();
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+        verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null),
+                anyInt(), any(byte[].class), eq(null), any(Message.class));
+
+        assertEquals(voiceMailNumber, mSIMRecordsUT.getVoiceMailNumber());
+        //if attempt to save bugAlphatag which sim don't support so we will make it null
+        assertEquals(null, mSIMRecordsUT.getVoiceMailAlphaTag());
+    }
+
+    @Test
+    @Ignore("b/256282468")
+    public void testSetVoiceMailNullNumber() throws InterruptedException {
+
+        String voiceMailNumber = null;
+        String alphaTag = "VoicemailAlphaTag"; // Messagerie vocale
+        final CountDownLatch latch = new CountDownLatch(2);
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation1");
+                    Message response = invocation.getArgument(2);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+
+        doAnswer(
+                invocation -> {
+                    int[] result = new int[3];
+                    result[0] = 32;
+                    result[1] = 32;
+                    result[2] = 1;
+                    Rlog.d("SIMRecordsTest", "Executing the test invocation2");
+                    Message response = invocation.getArgument(5);
+                    AsyncResult.forMessage(response, result, null);
+                    response.sendToTarget();
+                    latch.countDown();
+                    return null;
+                })
+                .when(mFhMock)
+                .updateEFLinearFixed(anyInt(), eq(null), anyInt(), any(byte[].class),
+                        eq(null), any(Message.class));
+
+
+        mSIMRecordsUT.setMailboxIndex(1);
+        Message message = Message.obtain(mTestHandler);
+        mSIMRecordsUT.setVoiceMailNumber(alphaTag, voiceMailNumber, message);
+        latch.await(5, TimeUnit.SECONDS);
+        mTestLooper.startAutoDispatch();
+        verify(mFhMock, times(1)).getEFLinearRecordSize(anyInt(), isNull(), any(Message.class));
+        verify(mFhMock, times(1)).updateEFLinearFixed(anyInt(), eq(null),
+                anyInt(), any(byte[].class), eq(null), any(Message.class));
+
+        assertEquals(null, mSIMRecordsUT.getVoiceMailNumber());
+        assertEquals(alphaTag, mSIMRecordsUT.getVoiceMailAlphaTag());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
index 90163cf..8caa248 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
@@ -180,7 +180,7 @@
         assertEquals(1, profiles.length);
         assertEquals("98760000000000543210", profiles[0].getIccid());
         assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState());
-        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F20");
+        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F24");
     }
 
     @Test
@@ -203,7 +203,7 @@
     @Test
     public void testEnabledOnEsimPort_GetAllProfiles() {
         int channel = mockLogicalChannelResponses(
-                "BF2D18A016E3145A0A896700000000004523019F7001009F2001019000");
+                "BF2D18A016E3145A0A896700000000004523019F7001009F2401019000");
 
         ResultCaptor<EuiccProfileInfo[]> resultCaptor = new ResultCaptor<>();
         mEuiccPort.mIsSupportsMultipleEnabledProfiles = true; // MEP capable
@@ -218,7 +218,7 @@
         // which is valid port. So the state should be enabled.
         // (As per MEP state and enabledOnEsimPort concept)
         assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, profiles[0].getState());
-        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F20");
+        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F24");
     }
 
     @Test
@@ -235,7 +235,7 @@
         EuiccProfileInfo[] profiles = resultCaptor.result;
         assertEquals(1, profiles.length);
         assertEquals(EuiccProfileInfo.PROFILE_STATE_DISABLED, profiles[0].getState());
-        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F20");
+        verifyStoreData(channel, "BF2D0F5C0D5A909192B79F709599BF769F24");
     }
 
     @Test
diff --git a/tools/tdi b/tools/tdi
deleted file mode 100755
index 940a83b..0000000
--- a/tools/tdi
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash
-# Telephony Debug Intents
-#set -x
-
-file_name='tdi'
-
-# Get the command as the first parameter
-cmd=$1
-shift
-
-function dc_errors()
-{
-    if [ "$1" == "" ]; then
-        echo "Usage: $file_name $cmd <dc> <count> <cause> <retry-time>"
-        echo "  <dc> must specifiy the DataConnection such as DC or GsmDC-1"
-        echo "  <count> := number of times to retry"
-        echo "  <cause> := From DataConnection.FailCause; such as -3 for SIGNAL_LOST"
-        echo "  <retry-time> := suggested retry time in milli-seconds"
-        exit
-    fi
-    the_DC=$1
-    echo "the_DC=$the_DC"
-
-    if [ "$2" != "" ]; then
-        counter="--ei counter $2";
-    fi
-    echo "counter=$counter"
-
-    if [ "$3" != "" ]; then
-        fail_cause="--ei fail_cause $3";
-    fi
-    echo "fail_cause=$fail_cause"
-
-    if [ "$4" != "" ]; then
-        suggested_retry_time="--ei suggested_retry_time $4";
-    fi
-    echo "suggested_retry_time=$suggested_retry_time"
-
-
-    adb shell am broadcast -a com.android.internal.telephony.$the_DC.action_fail_bringup $counter $fail_cause $suggested_retry_time
-}
-
-
-case ${cmd} in
-	dce) dc_errors "$@";;
-    # Add more commands in the future
-	*) echo 'Broadcast telephony debug intents'; echo 'usage: tdi [dce]'; echo '  dce=DC errors';;
-esac