Merge "Fix for updating the correct isUsingCarrierAggregation value"
diff --git a/Android.bp b/Android.bp
index 0fba2d1..9f5459b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,7 +51,10 @@
         "android.hardware.radio-V1.1-java",
         "android.hardware.radio-V1.2-java",
         "android.hardware.radio-V1.3-java",
+        "android.hardware.radio-V1.4-java",
         "android.hardware.radio.config-V1.0-java",
+        "android.hardware.radio.config-V1.1-java",
+        "android.hardware.radio.config-V1.2-java",
         "android.hardware.radio.deprecated-V1.0-java",
         "android.hidl.base-V1.0-java",
     ],
diff --git a/OWNERS b/OWNERS
index 585dd7e..284f0ec 100644
--- a/OWNERS
+++ b/OWNERS
@@ -10,3 +10,4 @@
 shuoq@google.com
 refuhoo@google.com
 paulye@google.com
+nazaninb@google.com
diff --git a/proto/src/carrierId.proto b/proto/src/carrierId.proto
index f67e9f6..0f5eba8 100644
--- a/proto/src/carrierId.proto
+++ b/proto/src/carrierId.proto
@@ -39,6 +39,11 @@
 
   // [Optional] Carrier attributes to match a carrier. At least one value is required.
   repeated CarrierAttribute carrier_attribute = 3;
+
+  // [Optional] A unique canonical number to represent its parent carrier. The parent-child
+  // relationship can be used to differentiate a single carrier by different networks,
+  // by prepaid v.s. postpaid  or even by 4G v.s. 3G plan.
+  optional int32 parent_canonical_id = 4;
 };
 
 // Attributes used to match a carrier.
@@ -79,5 +84,9 @@
   // [Optional] Prefix of Integrated Circuit Card Identifier. Read from subscription EF_ICCID.
   // Sample values: 894430, 894410
   repeated string iccid_prefix = 8;
+
+  // [Optional] Carrier Privilege Access Rule in hex string.
+  // Sample values: 61ed377e85d386a8dfee6b864bd85b0bfaa5af88
+  repeated string privilege_access_rule = 9;
 };
 
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index e441f71..1e7db0d 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -568,6 +568,21 @@
   RIL_E_INVALID_RESPONSE = 67;
 }
 
+// Errors returned by ImsService
+enum ImsServiceErrno {
+
+  // The operation error is unknown
+  IMS_E_UNKNOWN = 0;
+  // The operation has succeeded
+  IMS_E_SUCCESS = 1;
+  // Sending an SMS over IMS has failed. Do not retry over IMS again or fallback to CS.
+  IMS_E_SMS_SEND_STATUS_ERROR = 2;
+  // Sending an SMS over IMS has failed. Retry over IMS again.
+  IMS_E_SMS_SEND_STATUS_ERROR_RETRY = 3;
+  // Sending an SMS over IMS has failed. Fallback to sending the SMS over CS.
+  IMS_E_SMS_SEND_STATUS_ERROR_FALLBACK = 4;
+}
+
 // PDP_type values in TS 27.007 section 10.1.1.
 enum PdpType {
 
@@ -1307,17 +1322,27 @@
       // or old data call has removed.
       DATA_CALL_LIST_CHANGED = 5;
 
-      // Send a SMS message
+      // Send a SMS message over RIL
       SMS_SEND = 6;
 
-      // Message has been sent to network
+      // Message has been sent to network using RIL
       SMS_SEND_RESULT = 7;
 
-      // Notification about received SMS
+      // Notification about received SMS using RIL
       SMS_RECEIVED = 8;
 
       // CB message received
       CB_SMS_RECEIVED = 9;
+
+      // Send an SMS message over ImsService
+      SMS_SEND_IMS_SERVICE = 10;
+
+      // Receive an SMS message result over ImsService
+      SMS_SEND_RESULT_IMS_SERVICE = 11;
+
+      // Receive an SMS message over ImsService
+      SMS_RECEIVED_IMS_SERVICE = 12;
+
     }
 
     // Formats used to encode SMS messages
@@ -1417,6 +1442,7 @@
 
     // See 3GPP 27.005, 3.2.5 for GSM/UMTS,
     // 3GPP2 N.S0005 (IS-41C) Table 171 for CDMA,
+    // or ImsService for IMS specific error codes (using ImsService specific Event.Type)
     // -1 if unknown or not applicable
     optional int32 error_code = 10;
 
@@ -1428,6 +1454,9 @@
 
     // Cellbroadcast message content
     optional CBMessage cell_broadcast_message = 13;
+
+    // ImsService error code.
+    optional ImsServiceErrno imsError = 14;
   }
 
   // Time when session has started, in minutes since epoch,
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 853b85f..d5e7e0c 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -31,7 +31,7 @@
 public abstract class BaseCommands implements CommandsInterface {
     //***** Instance Variables
     protected Context mContext;
-    protected RadioState mState = RadioState.RADIO_UNAVAILABLE;
+    protected int mState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
     protected Object mStateMonitor = new Object();
 
     protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList();
@@ -117,7 +117,7 @@
     //***** CommandsInterface implementation
 
     @Override
-    public RadioState getRadioState() {
+    public @TelephonyManager.RadioPowerState int getRadioState() {
         return mState;
     }
 
@@ -154,7 +154,7 @@
         synchronized (mStateMonitor) {
             mOnRegistrants.add(r);
 
-            if (mState.isOn()) {
+            if (mState == TelephonyManager.RADIO_POWER_ON) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -174,7 +174,7 @@
         synchronized (mStateMonitor) {
             mAvailRegistrants.add(r);
 
-            if (mState.isAvailable()) {
+            if (mState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -194,7 +194,7 @@
         synchronized (mStateMonitor) {
             mNotAvailRegistrants.add(r);
 
-            if (!mState.isAvailable()) {
+            if (mState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -214,7 +214,8 @@
         synchronized (mStateMonitor) {
             mOffOrNotAvailRegistrants.add(r);
 
-            if (mState == RadioState.RADIO_OFF || !mState.isAvailable()) {
+            if (mState == TelephonyManager.RADIO_POWER_OFF
+                    || mState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                 r.notifyRegistrant(new AsyncResult(null, null, null));
             }
         }
@@ -791,12 +792,13 @@
      *
      * RadioState has 3 values : RADIO_OFF, RADIO_UNAVAILABLE, RADIO_ON.
      *
-     * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED
+     * @param newState new radio power state decoded from RIL_UNSOL_RADIO_STATE_CHANGED
      * @param forceNotifyRegistrants boolean indicating if registrants should be notified even if
      * there is no change in state
      */
-    protected void setRadioState(RadioState newState, boolean forceNotifyRegistrants) {
-        RadioState oldState;
+    protected void setRadioState(@TelephonyManager.RadioPowerState int newState,
+                                 boolean forceNotifyRegistrants) {
+        int oldState;
 
         synchronized (mStateMonitor) {
             oldState = mState;
@@ -809,21 +811,24 @@
 
             mRadioStateChangedRegistrants.notifyRegistrants();
 
-            if (mState.isAvailable() && !oldState.isAvailable()) {
+            if (mState != TelephonyManager.RADIO_POWER_UNAVAILABLE
+                    && oldState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                 mAvailRegistrants.notifyRegistrants();
             }
 
-            if (!mState.isAvailable() && oldState.isAvailable()) {
+            if (mState == TelephonyManager.RADIO_POWER_UNAVAILABLE
+                    && oldState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                 mNotAvailRegistrants.notifyRegistrants();
             }
 
-            if (mState.isOn() && !oldState.isOn()) {
+            if (mState == TelephonyManager.RADIO_POWER_ON
+                    && oldState != TelephonyManager.RADIO_POWER_ON) {
                 mOnRegistrants.notifyRegistrants();
             }
 
-            if ((!mState.isOn() || !mState.isAvailable())
-                && !((!oldState.isOn() || !oldState.isAvailable()))
-            ) {
+            if ((mState == TelephonyManager.RADIO_POWER_OFF
+                    || mState == TelephonyManager.RADIO_POWER_UNAVAILABLE)
+                    && (oldState == TelephonyManager.RADIO_POWER_ON)) {
                 mOffOrNotAvailRegistrants.notifyRegistrants();
             }
         }
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 064ecf4..a712d95 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -32,6 +32,7 @@
     public static final int ERROR_CALL_RINGING = 4;
     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
     CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index 23874e2..e48b3e5 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -209,7 +209,7 @@
         String[] convertMaps = null;
         CarrierConfigManager configManager = (CarrierConfigManager)
                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle bundle = configManager.getConfig();
+        PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId());
         if (bundle != null) {
             convertMaps =
                     bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY);
diff --git a/src/java/com/android/internal/telephony/CarrierIdentifier.java b/src/java/com/android/internal/telephony/CarrierIdentifier.java
deleted file mode 100644
index 1ff8971..0000000
--- a/src/java/com/android/internal/telephony/CarrierIdentifier.java
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * Copyright 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;
-
-import static android.provider.Telephony.CarrierId;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Telephony;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccProfile;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * CarrierIdentifier identifies the subscription carrier and returns a canonical carrier Id
- * and a user friendly carrier name. CarrierIdentifier reads subscription info and check against
- * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a
- * dedicated CarrierIdentifier.
- */
-public class CarrierIdentifier extends Handler {
-    private static final String LOG_TAG = CarrierIdentifier.class.getSimpleName();
-    private static final boolean DBG = true;
-    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
-
-    // events to trigger carrier identification
-    private static final int SIM_LOAD_EVENT             = 1;
-    private static final int SIM_ABSENT_EVENT           = 2;
-    private static final int SPN_OVERRIDE_EVENT         = 3;
-    private static final int ICC_CHANGED_EVENT          = 4;
-    private static final int PREFER_APN_UPDATE_EVENT    = 5;
-    private static final int CARRIER_ID_DB_UPDATE_EVENT = 6;
-
-    private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
-            Telephony.Carriers.CONTENT_URI, "preferapn");
-    private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
-
-    // cached matching rules based mccmnc to speed up resolution
-    private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
-    // cached carrier Id
-    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
-    // cached carrier name
-    private String mCarrierName;
-    // cached preferapn name
-    private String mPreferApn;
-    // cached service provider name. telephonyManager API returns empty string as default value.
-    // some carriers need to target devices with Empty SPN. In that case, carrier matching rule
-    // should specify "" spn explicitly.
-    private String mSpn = "";
-
-    private Context mContext;
-    private Phone mPhone;
-    private IccRecords mIccRecords;
-    private UiccProfile mUiccProfile;
-    private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
-    private final TelephonyManager mTelephonyMgr;
-    private final SubscriptionsChangedListener mOnSubscriptionsChangedListener =
-            new SubscriptionsChangedListener();
-
-    private final ContentObserver mContentObserver = new ContentObserver(this) {
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (CONTENT_URL_PREFER_APN.equals(uri.getLastPathSegment())) {
-                logd("onChange URI: " + uri);
-                sendEmptyMessage(PREFER_APN_UPDATE_EVENT);
-            } else if (CarrierId.All.CONTENT_URI.equals(uri)) {
-                logd("onChange URI: " + uri);
-                sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT);
-            }
-        }
-    };
-
-    private class SubscriptionsChangedListener
-            extends SubscriptionManager.OnSubscriptionsChangedListener {
-        final AtomicInteger mPreviousSubId =
-                new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        /**
-         * Callback invoked when there is any change to any SubscriptionInfo. Typically
-         * this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList}
-         */
-        @Override
-        public void onSubscriptionsChanged() {
-            int subId = mPhone.getSubId();
-            if (mPreviousSubId.getAndSet(subId) != subId) {
-                if (DBG) {
-                    logd("SubscriptionListener.onSubscriptionInfoChanged subId: "
-                            + mPreviousSubId);
-                }
-                if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                    sendEmptyMessage(SIM_LOAD_EVENT);
-                } else {
-                    sendEmptyMessage(SIM_ABSENT_EVENT);
-                }
-            }
-        }
-    }
-
-    public CarrierIdentifier(Phone phone) {
-        logd("Creating CarrierIdentifier[" + phone.getPhoneId() + "]");
-        mContext = phone.getContext();
-        mPhone = phone;
-        mTelephonyMgr = TelephonyManager.from(mContext);
-
-        // register events
-        mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false,
-                mContentObserver);
-        mContext.getContentResolver().registerContentObserver(
-                CarrierId.All.CONTENT_URI, false, mContentObserver);
-        SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
-                mOnSubscriptionsChangedListener);
-        UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
-    }
-
-    /**
-     * Entry point for the carrier identification.
-     *
-     *    1. SIM_LOAD_EVENT
-     *        This indicates that all SIM records has been loaded and its first entry point for the
-     *        carrier identification. Note, there are other attributes could be changed on the fly
-     *        like APN and SPN. We cached all carrier matching rules based on MCCMNC to speed
-     *        up carrier resolution on following trigger events.
-     *
-     *    2. PREFER_APN_UPDATE_EVENT
-     *        This indicates prefer apn has been changed. It could be triggered when user modified
-     *        APN settings or when default data connection first establishes on the current carrier.
-     *        We follow up on this by querying prefer apn sqlite and re-issue carrier identification
-     *        with the updated prefer apn name.
-     *
-     *    3. SPN_OVERRIDE_EVENT
-     *        This indicates that SPN value as been changed. It could be triggered from EF_SPN
-     *        record loading, carrier config override
-     *        {@link android.telephony.CarrierConfigManager#KEY_CARRIER_NAME_STRING}
-     *        or carrier app override {@link TelephonyManager#setOperatorBrandOverride(String)}.
-     *        we follow up this by checking the cached mSPN against the latest value and issue
-     *        carrier identification only if spn changes.
-     *
-     *    4. CARRIER_ID_DB_UPDATE_EVENT
-     *        This indicates that carrierIdentification database which stores all matching rules
-     *        has been updated. It could be triggered from OTA or assets update.
-     */
-    @Override
-    public void handleMessage(Message msg) {
-        if (VDBG) logd("handleMessage: " + msg.what);
-        switch (msg.what) {
-            case SIM_LOAD_EVENT:
-            case CARRIER_ID_DB_UPDATE_EVENT:
-                mSpn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
-                mPreferApn = getPreferApn();
-                loadCarrierMatchingRulesOnMccMnc();
-                break;
-            case SIM_ABSENT_EVENT:
-                mCarrierMatchingRulesOnMccMnc.clear();
-                mSpn = null;
-                mPreferApn = null;
-                updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
-                break;
-            case PREFER_APN_UPDATE_EVENT:
-                String preferApn = getPreferApn();
-                if (!equals(mPreferApn, preferApn, true)) {
-                    logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
-                    mPreferApn = preferApn;
-                    matchCarrier();
-                }
-                break;
-            case SPN_OVERRIDE_EVENT:
-                String spn = mTelephonyMgr.getSimOperatorNameForPhone(mPhone.getPhoneId());
-                if (!equals(mSpn, spn, true)) {
-                    logd("[updateSpn] from:" + mSpn + " to:" + spn);
-                    mSpn = spn;
-                    matchCarrier();
-                }
-                break;
-            case ICC_CHANGED_EVENT:
-                // all records used for carrier identification are from SimRecord
-                final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
-                        mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
-                if (mIccRecords != newIccRecords) {
-                    if (mIccRecords != null) {
-                        logd("Removing stale icc objects.");
-                        mIccRecords.unregisterForRecordsLoaded(this);
-                        mIccRecords.unregisterForRecordsOverride(this);
-                        mIccRecords = null;
-                    }
-                    if (newIccRecords != null) {
-                        logd("new Icc object");
-                        newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
-                        newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, null);
-                        mIccRecords = newIccRecords;
-                    }
-                }
-                // check UICC profile
-                final UiccProfile uiccProfile = UiccController.getInstance()
-                        .getUiccProfileForPhone(mPhone.getPhoneId());
-                if (mUiccProfile != uiccProfile) {
-                    if (mUiccProfile != null) {
-                        logd("unregister operatorBrandOverride");
-                        mUiccProfile.unregisterForOperatorBrandOverride(this);
-                        mUiccProfile = null;
-                    }
-                    if (uiccProfile != null) {
-                        logd("register operatorBrandOverride");
-                        uiccProfile.registerForOpertorBrandOverride(this, SPN_OVERRIDE_EVENT, null);
-                        mUiccProfile = uiccProfile;
-                    }
-                }
-                break;
-            default:
-                loge("invalid msg: " + msg.what);
-                break;
-        }
-    }
-
-    private void loadCarrierMatchingRulesOnMccMnc() {
-        try {
-            String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
-            Cursor cursor = mContext.getContentResolver().query(
-                    CarrierId.All.CONTENT_URI,
-                    /* projection */ null,
-                    /* selection */ CarrierId.All.MCCMNC + "=?",
-                    /* selectionArgs */ new String[]{mccmnc}, null);
-            try {
-                if (cursor != null) {
-                    if (VDBG) {
-                        logd("[loadCarrierMatchingRules]- " + cursor.getCount()
-                                + " Records(s) in DB" + " mccmnc: " + mccmnc);
-                    }
-                    mCarrierMatchingRulesOnMccMnc.clear();
-                    while (cursor.moveToNext()) {
-                        mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
-                    }
-                    matchCarrier();
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        } catch (Exception ex) {
-            loge("[loadCarrierMatchingRules]- ex: " + ex);
-        }
-    }
-
-    private String getPreferApn() {
-        Cursor cursor = mContext.getContentResolver().query(
-                Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
-                + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN},
-                /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
-        try {
-            if (cursor != null) {
-                if (VDBG) {
-                    logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB");
-                }
-                while (cursor.moveToNext()) {
-                    String apn = cursor.getString(cursor.getColumnIndexOrThrow(
-                            Telephony.Carriers.APN));
-                    logd("[getPreferApn]- " + apn);
-                    return apn;
-                }
-            }
-        } catch (Exception ex) {
-            loge("[getPreferApn]- exception: " + ex);
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-        return null;
-    }
-
-    private void updateCarrierIdAndName(int cid, String name) {
-        boolean update = false;
-        if (!equals(name, mCarrierName, true)) {
-            logd("[updateCarrierName] from:" + mCarrierName + " to:" + name);
-            mCarrierName = name;
-            update = true;
-        }
-        if (cid != mCarrierId) {
-            logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid);
-            mCarrierId = cid;
-            update = true;
-        }
-        if (update) {
-            mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:"
-                    + mCarrierName);
-            final Intent intent = new Intent(TelephonyManager
-                    .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
-            intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
-            intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
-            intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
-            mContext.sendBroadcast(intent);
-
-            // update current subscriptions
-            ContentValues cv = new ContentValues();
-            cv.put(CarrierId.CARRIER_ID, mCarrierId);
-            cv.put(CarrierId.CARRIER_NAME, mCarrierName);
-            mContext.getContentResolver().update(
-                    Uri.withAppendedPath(CarrierId.CONTENT_URI,
-                    Integer.toString(mPhone.getSubId())), cv, null, null);
-        }
-    }
-
-    private CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
-        return new CarrierMatchingRule(
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        CarrierId.All.IMSI_PREFIX_XPATTERN)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        CarrierId.All.ICCID_PREFIX)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
-                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)));
-    }
-
-    /**
-     * carrier matching attributes with corresponding cid
-     */
-    private static class CarrierMatchingRule {
-        /**
-         * These scores provide the hierarchical relationship between the attributes, intended to
-         * resolve conflicts in a deterministic way. The scores are constructed such that a match
-         * from a higher tier will beat any subsequent match which does not match at that tier,
-         * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule
-         * matches as the score helps to find the best match uniquely. e.g.,
-         * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all
-         * matches with subscription data. rule 2 wins with the highest matching score.
-         */
-        private static final int SCORE_MCCMNC          = 1 << 7;
-        private static final int SCORE_IMSI_PREFIX     = 1 << 6;
-        private static final int SCORE_ICCID_PREFIX    = 1 << 5;
-        private static final int SCORE_GID1            = 1 << 4;
-        private static final int SCORE_GID2            = 1 << 3;
-        private static final int SCORE_PLMN            = 1 << 2;
-        private static final int SCORE_SPN             = 1 << 1;
-        private static final int SCORE_APN             = 1 << 0;
-
-        private static final int SCORE_INVALID         = -1;
-
-        // carrier matching attributes
-        private String mMccMnc;
-        private String mImsiPrefixPattern;
-        private String mIccidPrefix;
-        private String mGid1;
-        private String mGid2;
-        private String mPlmn;
-        private String mSpn;
-        private String mApn;
-
-        // user-facing carrier name
-        private String mName;
-        // unique carrier id
-        private int mCid;
-
-        private int mScore = 0;
-
-        CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
-                String gid1, String gid2, String plmn, String spn, String apn, int cid,
-                String name) {
-            mMccMnc = mccmnc;
-            mImsiPrefixPattern = imsiPrefixPattern;
-            mIccidPrefix = iccidPrefix;
-            mGid1 = gid1;
-            mGid2 = gid2;
-            mPlmn = plmn;
-            mSpn = spn;
-            mApn = apn;
-            mCid = cid;
-            mName = name;
-        }
-
-        // Calculate matching score. Values which aren't set in the rule are considered "wild".
-        // All values in the rule must match in order for the subscription to be considered part of
-        // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier
-        // will beat any subsequent match which does not match at that tier. When there are multiple
-        // matches at the same tier, the match with highest score will be used.
-        public void match(CarrierMatchingRule subscriptionRule) {
-            mScore = 0;
-            if (mMccMnc != null) {
-                if (!CarrierIdentifier.equals(subscriptionRule.mMccMnc, mMccMnc, false)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_MCCMNC;
-            }
-            if (mImsiPrefixPattern != null) {
-                if (!imsiPrefixMatch(subscriptionRule.mImsiPrefixPattern, mImsiPrefixPattern)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_IMSI_PREFIX;
-            }
-            if (mIccidPrefix != null) {
-                if (!iccidPrefixMatch(subscriptionRule.mIccidPrefix, mIccidPrefix)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_ICCID_PREFIX;
-            }
-            if (mGid1 != null) {
-                // full string match. carrier matching should cover the corner case that gid1
-                // with garbage tail due to SIM manufacture issues.
-                if (!CarrierIdentifier.equals(subscriptionRule.mGid1, mGid1, true)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_GID1;
-            }
-            if (mGid2 != null) {
-                // full string match. carrier matching should cover the corner case that gid2
-                // with garbage tail due to SIM manufacture issues.
-                if (!CarrierIdentifier.equals(subscriptionRule.mGid2, mGid2, true)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_GID2;
-            }
-            if (mPlmn != null) {
-                if (!CarrierIdentifier.equals(subscriptionRule.mPlmn, mPlmn, true)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_PLMN;
-            }
-            if (mSpn != null) {
-                if (!CarrierIdentifier.equals(subscriptionRule.mSpn, mSpn, true)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_SPN;
-            }
-            if (mApn != null) {
-                if (!CarrierIdentifier.equals(subscriptionRule.mApn, mApn, true)) {
-                    mScore = SCORE_INVALID;
-                    return;
-                }
-                mScore += SCORE_APN;
-            }
-        }
-
-        private boolean imsiPrefixMatch(String imsi, String prefixXPattern) {
-            if (TextUtils.isEmpty(prefixXPattern)) return true;
-            if (TextUtils.isEmpty(imsi)) return false;
-            if (imsi.length() < prefixXPattern.length()) {
-                return false;
-            }
-            for (int i = 0; i < prefixXPattern.length(); i++) {
-                if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X')
-                        && (prefixXPattern.charAt(i) != imsi.charAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private boolean iccidPrefixMatch(String iccid, String prefix) {
-            if (iccid == null || prefix == null) {
-                return false;
-            }
-            return iccid.startsWith(prefix);
-        }
-
-        public String toString() {
-            return "[CarrierMatchingRule] -"
-                    + " mccmnc: " + mMccMnc
-                    + " gid1: " + mGid1
-                    + " gid2: " + mGid2
-                    + " plmn: " + mPlmn
-                    + " imsi_prefix: " + mImsiPrefixPattern
-                    + " iccid_prefix" + mIccidPrefix
-                    + " spn: " + mSpn
-                    + " apn: " + mApn
-                    + " name: " + mName
-                    + " cid: " + mCid
-                    + " score: " + mScore;
-        }
-    }
-
-    /**
-     * find the best matching carrier from candidates with matched MCCMNC and notify
-     * all interested parties on carrier id change.
-     */
-    private void matchCarrier() {
-        if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
-            logd("[matchCarrier]" + "skip before sim records loaded");
-            return;
-        }
-        final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
-        final String iccid = mPhone.getIccSerialNumber();
-        final String gid1 = mPhone.getGroupIdLevel1();
-        final String gid2 = mPhone.getGroupIdLevel2();
-        final String imsi = mPhone.getSubscriberId();
-        final String plmn = mPhone.getPlmn();
-        final String spn = mSpn;
-        final String apn = mPreferApn;
-
-        if (VDBG) {
-            logd("[matchCarrier]"
-                    + " mnnmnc:" + mccmnc
-                    + " gid1: " + gid1
-                    + " gid2: " + gid2
-                    + " imsi: " + Rlog.pii(LOG_TAG, imsi)
-                    + " iccid: " + Rlog.pii(LOG_TAG, iccid)
-                    + " plmn: " + plmn
-                    + " spn: " + spn
-                    + " apn: " + apn);
-        }
-
-        CarrierMatchingRule subscriptionRule = new CarrierMatchingRule(
-                mccmnc, imsi, iccid, gid1, gid2, plmn,  spn, apn,
-                TelephonyManager.UNKNOWN_CARRIER_ID, null);
-
-        int maxScore = CarrierMatchingRule.SCORE_INVALID;
-        CarrierMatchingRule maxRule = null;
-
-        for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
-            rule.match(subscriptionRule);
-            if (rule.mScore > maxScore) {
-                maxScore = rule.mScore;
-                maxRule = rule;
-            }
-        }
-
-        if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
-            logd("[matchCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
-                    + " name: " + null);
-            updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null);
-        } else {
-            logd("[matchCarrier] cid: " + maxRule.mCid + " name: " + maxRule.mName);
-            updateCarrierIdAndName(maxRule.mCid, maxRule.mName);
-        }
-
-        /*
-         * Write Carrier Identification Matching event, logging with the
-         * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
-         * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
-         * read mccmnc.
-         * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
-         * but the read gid1 is not matched within the highest-scored rule.
-         * 3) successfully found a matched carrier id in the provider.
-         * 4) use carrier list version to compare the unknown carrier ratio between each version.
-         */
-        String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
-                && !TextUtils.isEmpty(subscriptionRule.mGid1)) ? subscriptionRule.mGid1 : null;
-        String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
-                || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
-                && !TextUtils.isEmpty(subscriptionRule.mMccMnc)) ? subscriptionRule.mMccMnc : null;
-        TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
-                mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
-                unknownMccmncToLog, unknownGid1ToLog);
-    }
-
-    public int getCarrierListVersion() {
-        final Cursor cursor = mContext.getContentResolver().query(
-                Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
-                "get_version"), null, null, null);
-        cursor.moveToFirst();
-        return cursor.getInt(0);
-    }
-
-    public int getCarrierId() {
-        return mCarrierId;
-    }
-
-    public String getCarrierName() {
-        return mCarrierName;
-    }
-
-    private static boolean equals(String a, String b, boolean ignoreCase) {
-        if (a == null && b == null) return true;
-        if (a != null && b != null) {
-            return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b);
-        }
-        return false;
-    }
-
-    private static void logd(String str) {
-        Rlog.d(LOG_TAG, str);
-    }
-    private static void loge(String str) {
-        Rlog.e(LOG_TAG, str);
-    }
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-        ipw.println("mCarrierIdLocalLogs:");
-        ipw.increaseIndent();
-        mCarrierIdLocalLog.dump(fd, pw, args);
-        ipw.decreaseIndent();
-
-        ipw.println("mCarrierId: " + mCarrierId);
-        ipw.println("mCarrierName: " + mCarrierName);
-        ipw.println("version: " + getCarrierListVersion());
-
-        ipw.println("mCarrierMatchingRules on mccmnc: "
-                + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()));
-        ipw.increaseIndent();
-        for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
-            ipw.println(rule.toString());
-        }
-        ipw.decreaseIndent();
-
-        ipw.println("mSpn: " + mSpn);
-        ipw.println("mPreferApn: " + mPreferApn);
-        ipw.flush();
-    }
-}
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
new file mode 100644
index 0000000..0c7950f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -0,0 +1,983 @@
+/*
+ * Copyright 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;
+
+import static android.provider.Telephony.CarrierId;
+
+import android.annotation.NonNull;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Telephony;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id
+ * and a user friendly carrier name. CarrierResolver reads subscription info and check against
+ * all carrier matching rules stored in CarrierIdProvider. It is msim aware, each phone has a
+ * dedicated CarrierResolver.
+ */
+public class CarrierResolver extends Handler {
+    private static final String LOG_TAG = CarrierResolver.class.getSimpleName();
+    private static final boolean DBG = true;
+    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+    // events to trigger carrier identification
+    private static final int SIM_LOAD_EVENT             = 1;
+    private static final int ICC_CHANGED_EVENT          = 2;
+    private static final int PREFER_APN_UPDATE_EVENT    = 3;
+    private static final int CARRIER_ID_DB_UPDATE_EVENT = 4;
+
+    private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
+            Telephony.Carriers.CONTENT_URI, "preferapn");
+
+    // cached matching rules based mccmnc to speed up resolution
+    private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
+    // cached carrier Id
+    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    // cached precise carrier Id
+    private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    // cached MNO carrier Id. mno carrier shares the same mccmnc as cid and can be solely
+    // identified by mccmnc only. If there is no such mno carrier, mno carrier id equals to
+    // the cid.
+    private int mMnoCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    // cached carrier name
+    private String mCarrierName;
+    private String mPreciseCarrierName;
+    // cached preferapn name
+    private String mPreferApn;
+    // cached service provider name. telephonyManager API returns empty string as default value.
+    // some carriers need to target devices with Empty SPN. In that case, carrier matching rule
+    // should specify "" spn explicitly.
+    private String mSpn = "";
+
+    private Context mContext;
+    private Phone mPhone;
+    private IccRecords mIccRecords;
+    private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
+    private final TelephonyManager mTelephonyMgr;
+
+    private final ContentObserver mContentObserver = new ContentObserver(this) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (CONTENT_URL_PREFER_APN.equals(uri.getLastPathSegment())) {
+                logd("onChange URI: " + uri);
+                sendEmptyMessage(PREFER_APN_UPDATE_EVENT);
+            } else if (CarrierId.All.CONTENT_URI.equals(uri)) {
+                logd("onChange URI: " + uri);
+                sendEmptyMessage(CARRIER_ID_DB_UPDATE_EVENT);
+            }
+        }
+    };
+
+    public CarrierResolver(Phone phone) {
+        logd("Creating CarrierResolver[" + phone.getPhoneId() + "]");
+        mContext = phone.getContext();
+        mPhone = phone;
+        mTelephonyMgr = TelephonyManager.from(mContext);
+
+        // register events
+        mContext.getContentResolver().registerContentObserver(CONTENT_URL_PREFER_APN, false,
+                mContentObserver);
+        mContext.getContentResolver().registerContentObserver(
+                CarrierId.All.CONTENT_URI, false, mContentObserver);
+        UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
+    }
+
+    /**
+     * This is triggered from SubscriptionInfoUpdater after sim state change.
+     * The sequence of sim loading would be
+     *  1. ACTION_SUBINFO_CONTENT_CHANGE
+     *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
+     *  /ACTION_SIM_APPLICATION_STATE_CHANGED
+     *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
+     *
+     *  For SIM refresh either reset or file update, SubscriptionInfoUpdater will re-trigger
+     *  carrier identification with sim loaded state.
+     */
+    public void resolveSubscriptionCarrierId(String simState) {
+        logd("[resolveSubscriptionCarrierId] simState: " + simState);
+        switch (simState) {
+            case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
+                // only clear carrier id on absent to avoid transition to unknown carrier id during
+                // intermediate states of sim refresh
+                handleSimAbsent();
+                break;
+            case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
+                // intentional fall through from above case, treat locked same as loaded
+            case IccCardConstants.INTENT_VALUE_ICC_LOADED:
+                handleSimLoaded();
+                break;
+        }
+    }
+
+    private void handleSimLoaded() {
+        if (mIccRecords != null) {
+            /**
+             * returns empty string to be consistent with
+             * {@link TelephonyManager#getSimOperatorName()}
+             */
+            mSpn = (mIccRecords.getServiceProviderName() == null) ? ""
+                    : mIccRecords.getServiceProviderName();
+        } else {
+            loge("mIccRecords is null on SIM_LOAD_EVENT, could not get SPN");
+        }
+        mPreferApn = getPreferApn();
+        loadCarrierMatchingRulesOnMccMnc();
+    }
+
+    private void handleSimAbsent() {
+        mCarrierMatchingRulesOnMccMnc.clear();
+        mSpn = null;
+        mPreferApn = null;
+        updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null,
+                TelephonyManager.UNKNOWN_CARRIER_ID, null,
+                TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * Entry point for the carrier identification.
+     *
+     *    1. SIM_LOAD_EVENT
+     *        This indicates that all SIM records has been loaded and its first entry point for the
+     *        carrier identification. Note, there are other attributes could be changed on the fly
+     *        like APN. We cached all carrier matching rules based on MCCMNC to speed
+     *        up carrier resolution on following trigger events.
+     *
+     *    2. PREFER_APN_UPDATE_EVENT
+     *        This indicates prefer apn has been changed. It could be triggered when user modified
+     *        APN settings or when default data connection first establishes on the current carrier.
+     *        We follow up on this by querying prefer apn sqlite and re-issue carrier identification
+     *        with the updated prefer apn name.
+     *
+     *    3. CARRIER_ID_DB_UPDATE_EVENT
+     *        This indicates that carrierIdentification database which stores all matching rules
+     *        has been updated. It could be triggered from OTA or assets update.
+     */
+    @Override
+    public void handleMessage(Message msg) {
+        if (DBG) logd("handleMessage: " + msg.what);
+        switch (msg.what) {
+            case SIM_LOAD_EVENT:
+                handleSimLoaded();
+                break;
+            case CARRIER_ID_DB_UPDATE_EVENT:
+                loadCarrierMatchingRulesOnMccMnc();
+                break;
+            case PREFER_APN_UPDATE_EVENT:
+                String preferApn = getPreferApn();
+                if (!equals(mPreferApn, preferApn, true)) {
+                    logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
+                    mPreferApn = preferApn;
+                    matchSubscriptionCarrier();
+                }
+                break;
+            case ICC_CHANGED_EVENT:
+                // all records used for carrier identification are from SimRecord.
+                final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
+                        mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
+                if (mIccRecords != newIccRecords) {
+                    if (mIccRecords != null) {
+                        logd("Removing stale icc objects.");
+                        mIccRecords.unregisterForRecordsLoaded(this);
+                        mIccRecords.unregisterForRecordsOverride(this);
+                        mIccRecords = null;
+                    }
+                    if (newIccRecords != null) {
+                        logd("new Icc object");
+                        newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
+                        newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, null);
+                        mIccRecords = newIccRecords;
+                    }
+                }
+                break;
+            default:
+                loge("invalid msg: " + msg.what);
+                break;
+        }
+    }
+
+    private void loadCarrierMatchingRulesOnMccMnc() {
+        try {
+            String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
+            Cursor cursor = mContext.getContentResolver().query(
+                    CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ CarrierId.All.MCCMNC + "=?",
+                    /* selectionArgs */ new String[]{mccmnc}, null);
+            try {
+                if (cursor != null) {
+                    if (VDBG) {
+                        logd("[loadCarrierMatchingRules]- " + cursor.getCount()
+                                + " Records(s) in DB" + " mccmnc: " + mccmnc);
+                    }
+                    mCarrierMatchingRulesOnMccMnc.clear();
+                    while (cursor.moveToNext()) {
+                        mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
+                    }
+                    matchSubscriptionCarrier();
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } catch (Exception ex) {
+            loge("[loadCarrierMatchingRules]- ex: " + ex);
+        }
+    }
+
+    private String getCarrierNameFromId(int cid) {
+        try {
+            Cursor cursor = mContext.getContentResolver().query(
+                    CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ CarrierId.CARRIER_ID + "=?",
+                    /* selectionArgs */ new String[]{cid + ""}, null);
+            try {
+                if (cursor != null) {
+                    if (VDBG) {
+                        logd("[getCarrierNameFromId]- " + cursor.getCount()
+                                + " Records(s) in DB" + " cid: " + cid);
+                    }
+                    while (cursor.moveToNext()) {
+                        return cursor.getString(cursor.getColumnIndex(CarrierId.CARRIER_NAME));
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } catch (Exception ex) {
+            loge("[getCarrierNameFromId]- ex: " + ex);
+        }
+        return null;
+    }
+
+    private static List<CarrierMatchingRule> getCarrierMatchingRulesFromMccMnc(
+            @NonNull Context context, String mccmnc) {
+        List<CarrierMatchingRule> rules = new ArrayList<>();
+        try {
+            Cursor cursor = context.getContentResolver().query(
+                    CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ CarrierId.All.MCCMNC + "=?",
+                    /* selectionArgs */ new String[]{mccmnc}, null);
+            try {
+                if (cursor != null) {
+                    if (VDBG) {
+                        logd("[loadCarrierMatchingRules]- " + cursor.getCount()
+                                + " Records(s) in DB" + " mccmnc: " + mccmnc);
+                    }
+                    rules.clear();
+                    while (cursor.moveToNext()) {
+                        rules.add(makeCarrierMatchingRule(cursor));
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } catch (Exception ex) {
+            loge("[loadCarrierMatchingRules]- ex: " + ex);
+        }
+        return rules;
+    }
+
+    private String getPreferApn() {
+        Cursor cursor = mContext.getContentResolver().query(
+                Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn/subId/"
+                + mPhone.getSubId()), /* projection */ new String[]{Telephony.Carriers.APN},
+                /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
+        try {
+            if (cursor != null) {
+                if (VDBG) {
+                    logd("[getPreferApn]- " + cursor.getCount() + " Records(s) in DB");
+                }
+                while (cursor.moveToNext()) {
+                    String apn = cursor.getString(cursor.getColumnIndexOrThrow(
+                            Telephony.Carriers.APN));
+                    logd("[getPreferApn]- " + apn);
+                    return apn;
+                }
+            }
+        } catch (Exception ex) {
+            loge("[getPreferApn]- exception: " + ex);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return null;
+    }
+
+    private void updateCarrierIdAndName(int cid, String name,
+                                        int preciseCarrierId, String preciseCarrierName,
+                                        int mnoCid) {
+        boolean update = false;
+        if (!equals(name, mCarrierName, true)) {
+            logd("[updateCarrierName] from:" + mCarrierName + " to:" + name);
+            mCarrierName = name;
+            update = true;
+        }
+        if (cid != mCarrierId) {
+            logd("[updateCarrierId] from:" + mCarrierId + " to:" + cid);
+            mCarrierId = cid;
+            update = true;
+        }
+        if (mnoCid != mMnoCarrierId) {
+            logd("[updateMnoCarrierId] from:" + mMnoCarrierId + " to:" + mnoCid);
+            mMnoCarrierId = mnoCid;
+            update = true;
+        }
+        if (update) {
+            mCarrierIdLocalLog.log("[updateCarrierIdAndName] cid:" + mCarrierId + " name:"
+                    + mCarrierName + " mnoCid:" + mMnoCarrierId);
+            final Intent intent = new Intent(TelephonyManager
+                    .ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+            intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
+            intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
+            intent.putExtra(TelephonyManager.EXTRA_MNO_CARRIER_ID, mMnoCarrierId);
+            intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
+            mContext.sendBroadcast(intent);
+
+            // notify content observers for carrier id change event
+            ContentValues cv = new ContentValues();
+            cv.put(CarrierId.CARRIER_ID, mCarrierId);
+            cv.put(CarrierId.CARRIER_NAME, mCarrierName);
+            cv.put(CarrierId.MNO_CARRIER_ID, mMnoCarrierId);
+            mContext.getContentResolver().update(
+                    Uri.withAppendedPath(CarrierId.CONTENT_URI,
+                            Integer.toString(mPhone.getSubId())), cv, null, null);
+        }
+
+        update = false;
+        if (preciseCarrierId != mPreciseCarrierId) {
+            logd("[updatePreciseCarrierId] from:" + mPreciseCarrierId + " to:"
+                    + preciseCarrierId);
+            mPreciseCarrierId = preciseCarrierId;
+            update = true;
+        }
+        if (preciseCarrierName != mPreciseCarrierName) {
+            logd("[updatePreciseCarrierName] from:" + mPreciseCarrierName + " to:"
+                    + preciseCarrierName);
+            mPreciseCarrierName = preciseCarrierName;
+            update = true;
+        }
+        if (update) {
+            mCarrierIdLocalLog.log("[updatePreciseCarrierIdAndName] cid:" + mPreciseCarrierId
+                    + " name:" + mPreciseCarrierName);
+            final Intent intent = new Intent(TelephonyManager
+                    .ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED);
+            intent.putExtra(TelephonyManager.EXTRA_PRECISE_CARRIER_ID, mPreciseCarrierId);
+            intent.putExtra(TelephonyManager.EXTRA_PRECISE_CARRIER_NAME, mPreciseCarrierName);
+            intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
+            mContext.sendBroadcast(intent);
+
+            // notify content observers for precise carrier id change event.
+            ContentValues cv = new ContentValues();
+            cv.put(CarrierId.PRECISE_CARRIER_ID, mPreciseCarrierId);
+            cv.put(CarrierId.PRECISE_CARRIER_ID_NAME, mPreciseCarrierName);
+            mContext.getContentResolver().update(
+                    Telephony.CarrierId.getPreciseCarrierIdUriForSubscriptionId(mPhone.getSubId()),
+                    cv, null, null);
+        }
+    }
+
+    private static CarrierMatchingRule makeCarrierMatchingRule(Cursor cursor) {
+        String certs = cursor.getString(
+                cursor.getColumnIndexOrThrow(CarrierId.All.PRIVILEGE_ACCESS_RULE));
+        return new CarrierMatchingRule(
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.MCCMNC)),
+                cursor.getString(cursor.getColumnIndexOrThrow(
+                        CarrierId.All.IMSI_PREFIX_XPATTERN)),
+                cursor.getString(cursor.getColumnIndexOrThrow(
+                        CarrierId.All.ICCID_PREFIX)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID1)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.GID2)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.PLMN)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.SPN)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.All.APN)),
+                (TextUtils.isEmpty(certs) ? null : new ArrayList<>(Arrays.asList(certs))),
+                cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_ID)),
+                cursor.getString(cursor.getColumnIndexOrThrow(CarrierId.CARRIER_NAME)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(CarrierId.PARENT_CARRIER_ID)));
+    }
+
+    /**
+     * carrier matching attributes with corresponding cid
+     */
+    private static class CarrierMatchingRule {
+        /**
+         * These scores provide the hierarchical relationship between the attributes, intended to
+         * resolve conflicts in a deterministic way. The scores are constructed such that a match
+         * from a higher tier will beat any subsequent match which does not match at that tier,
+         * so MCCMNC beats everything else. This avoids problems when two (or more) carriers rule
+         * matches as the score helps to find the best match uniquely. e.g.,
+         * rule 1 {mccmnc, imsi} rule 2 {mccmnc, imsi, gid1} and rule 3 {mccmnc, imsi, gid2} all
+         * matches with subscription data. rule 2 wins with the highest matching score.
+         */
+        private static final int SCORE_MCCMNC                   = 1 << 8;
+        private static final int SCORE_IMSI_PREFIX              = 1 << 7;
+        private static final int SCORE_ICCID_PREFIX             = 1 << 6;
+        private static final int SCORE_GID1                     = 1 << 5;
+        private static final int SCORE_GID2                     = 1 << 4;
+        private static final int SCORE_PLMN                     = 1 << 3;
+        private static final int SCORE_PRIVILEGE_ACCESS_RULE    = 1 << 2;
+        private static final int SCORE_SPN                      = 1 << 1;
+        private static final int SCORE_APN                      = 1 << 0;
+
+        private static final int SCORE_INVALID                  = -1;
+
+        // carrier matching attributes
+        private final String mMccMnc;
+        private final String mImsiPrefixPattern;
+        private final String mIccidPrefix;
+        private final String mGid1;
+        private final String mGid2;
+        private final String mPlmn;
+        private final String mSpn;
+        private final String mApn;
+        // there can be multiple certs configured in the UICC
+        private final List<String> mPrivilegeAccessRule;
+
+        // user-facing carrier name
+        private String mName;
+        // unique carrier id
+        private int mCid;
+        // unique parent carrier id
+        private int mParentCid;
+
+        private int mScore = 0;
+
+        private CarrierMatchingRule(String mccmnc, String imsiPrefixPattern, String iccidPrefix,
+                String gid1, String gid2, String plmn, String spn, String apn,
+                List<String> privilegeAccessRule, int cid, String name, int parentCid) {
+            mMccMnc = mccmnc;
+            mImsiPrefixPattern = imsiPrefixPattern;
+            mIccidPrefix = iccidPrefix;
+            mGid1 = gid1;
+            mGid2 = gid2;
+            mPlmn = plmn;
+            mSpn = spn;
+            mApn = apn;
+            mPrivilegeAccessRule = privilegeAccessRule;
+            mCid = cid;
+            mName = name;
+            mParentCid = parentCid;
+        }
+
+        private CarrierMatchingRule(CarrierMatchingRule rule) {
+            mMccMnc = rule.mMccMnc;
+            mImsiPrefixPattern = rule.mImsiPrefixPattern;
+            mIccidPrefix = rule.mIccidPrefix;
+            mGid1 = rule.mGid1;
+            mGid2 = rule.mGid2;
+            mPlmn = rule.mPlmn;
+            mSpn = rule.mSpn;
+            mApn = rule.mApn;
+            mPrivilegeAccessRule = rule.mPrivilegeAccessRule;
+            mCid = rule.mCid;
+            mName = rule.mName;
+            mParentCid = rule.mParentCid;
+        }
+
+        // Calculate matching score. Values which aren't set in the rule are considered "wild".
+        // All values in the rule must match in order for the subscription to be considered part of
+        // the carrier. Otherwise, a invalid score -1 will be assigned. A match from a higher tier
+        // will beat any subsequent match which does not match at that tier. When there are multiple
+        // matches at the same tier, the match with highest score will be used.
+        public void match(CarrierMatchingRule subscriptionRule) {
+            mScore = 0;
+            if (mMccMnc != null) {
+                if (!CarrierResolver.equals(subscriptionRule.mMccMnc, mMccMnc, false)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_MCCMNC;
+            }
+            if (mImsiPrefixPattern != null) {
+                if (!imsiPrefixMatch(subscriptionRule.mImsiPrefixPattern, mImsiPrefixPattern)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_IMSI_PREFIX;
+            }
+            if (mIccidPrefix != null) {
+                if (!iccidPrefixMatch(subscriptionRule.mIccidPrefix, mIccidPrefix)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_ICCID_PREFIX;
+            }
+            if (mGid1 != null) {
+                if (!gidMatch(subscriptionRule.mGid1, mGid1)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_GID1;
+            }
+            if (mGid2 != null) {
+                if (!gidMatch(subscriptionRule.mGid2, mGid2)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_GID2;
+            }
+            if (mPlmn != null) {
+                if (!CarrierResolver.equals(subscriptionRule.mPlmn, mPlmn, true)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_PLMN;
+            }
+            if (mSpn != null) {
+                if (!CarrierResolver.equals(subscriptionRule.mSpn, mSpn, true)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_SPN;
+            }
+
+            if (mPrivilegeAccessRule != null && !mPrivilegeAccessRule.isEmpty()) {
+                if (!carrierPrivilegeRulesMatch(subscriptionRule.mPrivilegeAccessRule,
+                        mPrivilegeAccessRule)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_PRIVILEGE_ACCESS_RULE;
+            }
+
+            if (mApn != null) {
+                if (!CarrierResolver.equals(subscriptionRule.mApn, mApn, true)) {
+                    mScore = SCORE_INVALID;
+                    return;
+                }
+                mScore += SCORE_APN;
+            }
+        }
+
+        private boolean imsiPrefixMatch(String imsi, String prefixXPattern) {
+            if (TextUtils.isEmpty(prefixXPattern)) return true;
+            if (TextUtils.isEmpty(imsi)) return false;
+            if (imsi.length() < prefixXPattern.length()) {
+                return false;
+            }
+            for (int i = 0; i < prefixXPattern.length(); i++) {
+                if ((prefixXPattern.charAt(i) != 'x') && (prefixXPattern.charAt(i) != 'X')
+                        && (prefixXPattern.charAt(i) != imsi.charAt(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private boolean iccidPrefixMatch(String iccid, String prefix) {
+            if (iccid == null || prefix == null) {
+                return false;
+            }
+            return iccid.startsWith(prefix);
+        }
+
+        // We are doing prefix and case insensitive match.
+        // 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());
+        }
+
+        private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
+                                                   List<String> certs) {
+            if (certsFromSubscription == null || certsFromSubscription.isEmpty()) {
+                return false;
+            }
+            for (String cert : certs) {
+                for (String certFromSubscription : certsFromSubscription) {
+                    if (!TextUtils.isEmpty(cert)
+                            && cert.equalsIgnoreCase(certFromSubscription)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public String toString() {
+            return "[CarrierMatchingRule] -"
+                    + " mccmnc: " + mMccMnc
+                    + " gid1: " + mGid1
+                    + " gid2: " + mGid2
+                    + " plmn: " + mPlmn
+                    + " imsi_prefix: " + mImsiPrefixPattern
+                    + " iccid_prefix" + mIccidPrefix
+                    + " spn: " + mSpn
+                    + " privilege_access_rule: " + mPrivilegeAccessRule
+                    + " apn: " + mApn
+                    + " name: " + mName
+                    + " cid: " + mCid
+                    + " score: " + mScore;
+        }
+    }
+
+    private CarrierMatchingRule getSubscriptionMatchingRule() {
+        final String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
+        final String iccid = mPhone.getIccSerialNumber();
+        final String gid1 = mPhone.getGroupIdLevel1();
+        final String gid2 = mPhone.getGroupIdLevel2();
+        final String imsi = mPhone.getSubscriberId();
+        final String plmn = mPhone.getPlmn();
+        final String spn = mSpn;
+        final String apn = mPreferApn;
+        final List<String> accessRules = mTelephonyMgr.getCertsFromCarrierPrivilegeAccessRules();
+
+        if (VDBG) {
+            logd("[matchSubscriptionCarrier]"
+                    + " mnnmnc:" + mccmnc
+                    + " gid1: " + gid1
+                    + " gid2: " + gid2
+                    + " imsi: " + Rlog.pii(LOG_TAG, imsi)
+                    + " iccid: " + Rlog.pii(LOG_TAG, iccid)
+                    + " plmn: " + plmn
+                    + " spn: " + spn
+                    + " apn: " + apn
+                    + " accessRules: " + ((accessRules != null) ? accessRules : null));
+        }
+        return new CarrierMatchingRule(
+                mccmnc, imsi, iccid, gid1, gid2, plmn, spn, apn, accessRules,
+                TelephonyManager.UNKNOWN_CARRIER_ID, null,
+                TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * find the best matching carrier from candidates with matched subscription MCCMNC.
+     */
+    private void matchSubscriptionCarrier() {
+        if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
+            logd("[matchSubscriptionCarrier]" + "skip before sim records loaded");
+            return;
+        }
+        int maxScore = CarrierMatchingRule.SCORE_INVALID;
+        /**
+         * For child-parent relationship. either child and parent have the same matching
+         * score, or child's matching score > parents' matching score.
+         */
+        CarrierMatchingRule maxRule = null;
+        CarrierMatchingRule maxRuleParent = null;
+        /**
+         * matching rule with mccmnc only. If mnoRule is found, then mno carrier id equals to the
+         * cid from mnoRule. otherwise, mno carrier id is same as cid.
+         */
+        CarrierMatchingRule mnoRule = null;
+        CarrierMatchingRule subscriptionRule = getSubscriptionMatchingRule();
+
+        for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
+            rule.match(subscriptionRule);
+            if (rule.mScore > maxScore) {
+                maxScore = rule.mScore;
+                maxRule = rule;
+                maxRuleParent = rule;
+            } else if (maxScore > CarrierMatchingRule.SCORE_INVALID && rule.mScore == maxScore) {
+                // to handle the case that child parent has the same matching score, we need to
+                // differentiate who is child who is parent.
+                if (rule.mParentCid == maxRule.mCid) {
+                    maxRule = rule;
+                } else if (maxRule.mParentCid == rule.mCid) {
+                    maxRuleParent = rule;
+                }
+            }
+            if (rule.mScore == CarrierMatchingRule.SCORE_MCCMNC) {
+                mnoRule = rule;
+            }
+        }
+        if (maxScore == CarrierMatchingRule.SCORE_INVALID) {
+            logd("[matchSubscriptionCarrier - no match] cid: " + TelephonyManager.UNKNOWN_CARRIER_ID
+                    + " name: " + null);
+            updateCarrierIdAndName(TelephonyManager.UNKNOWN_CARRIER_ID, null,
+                    TelephonyManager.UNKNOWN_CARRIER_ID, null,
+                    TelephonyManager.UNKNOWN_CARRIER_ID);
+        } else {
+            // if there is a single matching result, check if this rule has parent cid assigned.
+            if ((maxRule == maxRuleParent)
+                    && maxRule.mParentCid != TelephonyManager.UNKNOWN_CARRIER_ID) {
+                maxRuleParent = new CarrierMatchingRule(maxRule);
+                maxRuleParent.mCid = maxRuleParent.mParentCid;
+                maxRuleParent.mName = getCarrierNameFromId(maxRuleParent.mCid);
+            }
+            logd("[matchSubscriptionCarrier] precise cid: " + maxRule.mCid + " precise name: "
+                    + maxRule.mName +" cid: " + maxRuleParent.mCid
+                    + " name: " + maxRuleParent.mName);
+            updateCarrierIdAndName(maxRuleParent.mCid, maxRuleParent.mName,
+                    maxRule.mCid, maxRule.mName,
+                    (mnoRule == null) ? maxRule.mCid : mnoRule.mCid);
+        }
+
+        /*
+         * Write Carrier Identification Matching event, logging with the
+         * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
+         * 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
+         * read mccmnc.
+         * 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
+         * but the read gid1 is not matched within the highest-scored rule.
+         * 3) successfully found a matched carrier id in the provider.
+         * 4) use carrier list version to compare the unknown carrier ratio between each version.
+         */
+        String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
+                && !TextUtils.isEmpty(subscriptionRule.mGid1)) ? subscriptionRule.mGid1 : null;
+        String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
+                || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
+                && !TextUtils.isEmpty(subscriptionRule.mMccMnc)) ? subscriptionRule.mMccMnc : null;
+        TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
+                mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
+                unknownMccmncToLog, unknownGid1ToLog);
+    }
+
+    public int getCarrierListVersion() {
+        final Cursor cursor = mContext.getContentResolver().query(
+                Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
+                "get_version"), null, null, null);
+        cursor.moveToFirst();
+        return cursor.getInt(0);
+    }
+
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+    /**
+     * Returns fine-grained carrier id of the current subscription. Carrier ids with a valid parent
+     * id are precise carrier ids.
+     * The precise carrier id can be used to further differentiate a carrier by different
+     * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
+     * carrier id but can have multiple precise carrier id. e.g,
+     * {@link #getCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while
+     * {@link #getPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based on the
+     * current underlying network.
+     * For carriers without any fine-grained carrier ids, return {@link #getCarrierId()}
+     */
+    public int getPreciseCarrierId() {
+        return mPreciseCarrierId;
+    }
+
+    public String getCarrierName() {
+        return mCarrierName;
+    }
+
+    public String getPreciseCarrierName() {
+        return mPreciseCarrierName;
+    }
+
+    public int getMnoCarrierId() {
+        return mMnoCarrierId;
+    }
+
+    /**
+     * a util function to convert carrierIdentifier to the best matching carrier id.
+     *
+     * @return the best matching carrier id.
+     */
+    public static int getCarrierIdFromIdentifier(@NonNull Context context,
+                                                 @NonNull CarrierIdentifier carrierIdentifier) {
+        final String mccmnc = carrierIdentifier.getMcc() + carrierIdentifier.getMnc();
+        final String gid1 = carrierIdentifier.getGid1();
+        final String gid2 = carrierIdentifier.getGid2();
+        final String imsi = carrierIdentifier.getImsi();
+        final String spn = carrierIdentifier.getSpn();
+        if (VDBG) {
+            logd("[getCarrierIdFromIdentifier]"
+                    + " mnnmnc:" + mccmnc
+                    + " gid1: " + gid1
+                    + " gid2: " + gid2
+                    + " imsi: " + Rlog.pii(LOG_TAG, imsi)
+                    + " spn: " + spn);
+        }
+        // assign null to other fields which are not supported by carrierIdentifier.
+        CarrierMatchingRule targetRule =
+                new CarrierMatchingRule(mccmnc, imsi, null, gid1, gid2, null,
+                        spn, null, null,
+                        TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION, null,
+                        TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+        int maxScore = CarrierMatchingRule.SCORE_INVALID;
+        List<CarrierMatchingRule> rules = getCarrierMatchingRulesFromMccMnc(
+                context, targetRule.mMccMnc);
+        for (CarrierMatchingRule rule : rules) {
+            rule.match(targetRule);
+            if (rule.mScore > maxScore) {
+                maxScore = rule.mScore;
+                carrierId = rule.mCid;
+            }
+        }
+        return carrierId;
+    }
+
+    /**
+     * a util function to convert {mccmnc, mvno_type, mvno_data} to all matching carrier ids.
+     *
+     * @return a list of id with matching {mccmnc, mvno_type, mvno_data}
+     */
+    public static List<Integer> getCarrierIdsFromApnQuery(@NonNull Context context,
+                                                          String mccmnc, String mvnoCase,
+                                                          String mvnoData) {
+        String selection = CarrierId.All.MCCMNC + "=" + mccmnc;
+        // build the proper query
+        if ("spn".equals(mvnoCase) && mvnoData != null) {
+            selection += " AND " + CarrierId.All.SPN + "='" + mvnoData + "'";
+        } else if ("imsi".equals(mvnoCase) && mvnoData != null) {
+            selection += " AND " + CarrierId.All.IMSI_PREFIX_XPATTERN + "='" + mvnoData + "'";
+        } else if ("gid1".equals(mvnoCase) && mvnoData != null) {
+            selection += " AND " + CarrierId.All.GID1 + "='" + mvnoData + "'";
+        } else if ("gid2".equals(mvnoCase) && mvnoData != null) {
+            selection += " AND " + CarrierId.All.GID2 + "='" + mvnoData +"'";
+        } else {
+            logd("mvno case empty or other invalid values");
+        }
+
+        List<Integer> ids = new ArrayList<>();
+        try {
+            Cursor cursor = context.getContentResolver().query(
+                    CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ selection,
+                    /* selectionArgs */ null, null);
+            try {
+                if (cursor != null) {
+                    if (VDBG) {
+                        logd("[getCarrierIdsFromApnQuery]- " + cursor.getCount()
+                                + " Records(s) in DB");
+                    }
+                    while (cursor.moveToNext()) {
+                        int cid = cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID));
+                        if (!ids.contains(cid)) {
+                            ids.add(cid);
+                        }
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } catch (Exception ex) {
+            loge("[getCarrierIdsFromApnQuery]- ex: " + ex);
+        }
+        logd(selection + " " + ids);
+        return ids;
+    }
+
+    // static helper function to get carrier id from mccmnc
+    public static int getCarrierIdFromMccMnc(@NonNull Context context, String mccmnc) {
+        try {
+            Cursor cursor = context.getContentResolver().query(
+                    CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ CarrierId.All.MCCMNC + "=? AND "
+                            + CarrierId.All.GID1 + " is NULL AND "
+                            + CarrierId.All.GID2 + " is NULL AND "
+                            + CarrierId.All.IMSI_PREFIX_XPATTERN + " is NULL AND "
+                            + CarrierId.All.SPN + " is NULL AND "
+                            + CarrierId.All.ICCID_PREFIX + " is NULL AND "
+                            + CarrierId.All.PLMN + " is NULL AND "
+                            + CarrierId.All.PRIVILEGE_ACCESS_RULE + " is NULL AND "
+                            + CarrierId.All.APN + " is NULL",
+                    /* selectionArgs */ new String[]{mccmnc},
+                    null);
+            try {
+                if (cursor != null) {
+                    if (VDBG) {
+                        logd("[getCarrierIdFromMccMnc]- " + cursor.getCount()
+                                + " Records(s) in DB" + " mccmnc: " + mccmnc);
+                    }
+                    while (cursor.moveToNext()) {
+                        return cursor.getInt(cursor.getColumnIndex(CarrierId.CARRIER_ID));
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } catch (Exception ex) {
+            loge("[getCarrierIdFromMccMnc]- ex: " + ex);
+        }
+        return TelephonyManager.UNKNOWN_CARRIER_ID;
+    }
+
+    private static boolean equals(String a, String b, boolean ignoreCase) {
+        if (a == null && b == null) return true;
+        if (a != null && b != null) {
+            return (ignoreCase) ? a.equalsIgnoreCase(b) : a.equals(b);
+        }
+        return false;
+    }
+
+    private static void logd(String str) {
+        Rlog.d(LOG_TAG, str);
+    }
+    private static void loge(String str) {
+        Rlog.e(LOG_TAG, str);
+    }
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("mCarrierResolverLocalLogs:");
+        ipw.increaseIndent();
+        mCarrierIdLocalLog.dump(fd, pw, args);
+        ipw.decreaseIndent();
+
+        ipw.println("mCarrierId: " + mCarrierId);
+        ipw.println("mPreciseCarrierId: " + mPreciseCarrierId);
+        ipw.println("mMnoCarrierId: " + mMnoCarrierId);
+        ipw.println("mCarrierName: " + mCarrierName);
+        ipw.println("mPreciseCarrierName: " + mPreciseCarrierName);
+        ipw.println("version: " + getCarrierListVersion());
+
+        ipw.println("mCarrierMatchingRules on mccmnc: "
+                + mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId()));
+        ipw.increaseIndent();
+        for (CarrierMatchingRule rule : mCarrierMatchingRulesOnMccMnc) {
+            ipw.println(rule.toString());
+        }
+        ipw.decreaseIndent();
+
+        ipw.println("mSpn: " + mSpn);
+        ipw.println("mPreferApn: " + mPreferApn);
+        ipw.flush();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index f3bc1fd..5a63613 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -21,6 +21,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.service.carrier.CarrierMessagingService;
 import android.service.carrier.ICarrierMessagingCallback;
@@ -35,8 +37,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Filters incoming SMS with carrier services.
@@ -44,6 +48,11 @@
  */
 public class CarrierServicesSmsFilter {
     protected static final boolean DBG = true;
+    /** onFilterComplete is not called. */
+    public static final int EVENT_ON_FILTER_COMPLETE_NOT_CALLED = 1;
+
+    /** onFilterComplete timeout. */
+    public static final int FILTER_COMPLETE_TIMEOUT_MS = 60000; //10 minutes
 
     private final Context mContext;
     private final Phone mPhone;
@@ -52,6 +61,9 @@
     private final String mPduFormat;
     private final CarrierServicesSmsFilterCallbackInterface mCarrierServicesSmsFilterCallback;
     private final String mLogTag;
+    private final CallbackTimeoutHandler mCallbackTimeoutHandler;
+    private FilterAggregator mFilterAggregator;
+
 
     @VisibleForTesting
     public CarrierServicesSmsFilter(
@@ -69,6 +81,7 @@
         mPduFormat = pduFormat;
         mCarrierServicesSmsFilterCallback = carrierServicesSmsFilterCallback;
         mLogTag = logTag;
+        mCallbackTimeoutHandler = new CallbackTimeoutHandler();
     }
 
     /**
@@ -86,9 +99,19 @@
         if (carrierImsPackage != null) {
             smsFilterPackages.add(carrierImsPackage);
         }
-        FilterAggregator filterAggregator = new FilterAggregator(smsFilterPackages.size());
+
+        if (mFilterAggregator != null) {
+            String errMsg = "Cannot reuse the same CarrierServiceSmsFilter object for filtering.";
+            loge(errMsg);
+            throw new RuntimeException(errMsg);
+        }
+
+        mFilterAggregator = new FilterAggregator(smsFilterPackages.size());
+        //start the timer
+        mCallbackTimeoutHandler.sendMessageDelayed(mCallbackTimeoutHandler
+                .obtainMessage(EVENT_ON_FILTER_COMPLETE_NOT_CALLED), FILTER_COMPLETE_TIMEOUT_MS);
         for (String smsFilterPackage : smsFilterPackages) {
-            filterWithPackage(smsFilterPackage, filterAggregator);
+            filterWithPackage(smsFilterPackage, mFilterAggregator);
         }
         boolean handled = smsFilterPackages.size() > 0;
         return handled;
@@ -127,6 +150,8 @@
         CarrierSmsFilter smsFilter = new CarrierSmsFilter(mPdus, mDestPort, mPduFormat);
         CarrierSmsFilterCallback smsFilterCallback =
                 new CarrierSmsFilterCallback(filterAggregator, smsFilter);
+        filterAggregator.addToCallbacks(smsFilterCallback);
+
         smsFilter.filterSms(packageName, smsFilterCallback);
     }
 
@@ -228,11 +253,13 @@
     private final class CarrierSmsFilterCallback extends ICarrierMessagingCallback.Stub {
         private final FilterAggregator mFilterAggregator;
         private final CarrierMessagingServiceManager mCarrierMessagingServiceManager;
+        private boolean mIsOnFilterCompleteCalled;
 
         CarrierSmsFilterCallback(FilterAggregator filterAggregator,
-                                 CarrierMessagingServiceManager carrierMessagingServiceManager) {
+                CarrierMessagingServiceManager carrierMessagingServiceManager) {
             mFilterAggregator = filterAggregator;
             mCarrierMessagingServiceManager = carrierMessagingServiceManager;
+            mIsOnFilterCompleteCalled = false;
         }
 
         /**
@@ -240,8 +267,13 @@
          */
         @Override
         public void onFilterComplete(int result) {
-            mCarrierMessagingServiceManager.disposeConnection(mContext);
-            mFilterAggregator.onFilterComplete(result);
+            // in the case that timeout has already passed and triggered, but the initial callback
+            // is run afterwards, we should not follow through
+            if (!mIsOnFilterCompleteCalled) {
+                mIsOnFilterCompleteCalled = true;
+                mCarrierMessagingServiceManager.disposeConnection(mContext);
+                mFilterAggregator.onFilterComplete(result);
+            }
         }
 
         @Override
@@ -268,10 +300,12 @@
     private final class FilterAggregator {
         private final Object mFilterLock = new Object();
         private int mNumPendingFilters;
+        private final Set<CarrierSmsFilterCallback> mCallbacks;
         private int mFilterResult;
 
         FilterAggregator(int numFilters) {
             mNumPendingFilters = numFilters;
+            mCallbacks = new HashSet<>();
             mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
         }
 
@@ -289,6 +323,9 @@
                         // return back to the CarrierMessagingService, restore the calling identity.
                         Binder.restoreCallingIdentity(token);
                     }
+                    //all onFilterCompletes called before timeout has triggered
+                    //remove the pending message
+                    mCallbackTimeoutHandler.removeMessages(EVENT_ON_FILTER_COMPLETE_NOT_CALLED);
                 }
             }
         }
@@ -296,5 +333,34 @@
         private void combine(int result) {
             mFilterResult = mFilterResult | result;
         }
+
+        private void addToCallbacks(CarrierSmsFilterCallback callback) {
+            mCallbacks.add(callback);
+        }
+
+    }
+
+    protected final class CallbackTimeoutHandler extends Handler {
+
+        private static final boolean DBG = true;
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (DBG) {
+                log("CallbackTimeoutHandler handleMessage(" + msg.what + ")");
+            }
+
+            switch(msg.what) {
+                case EVENT_ON_FILTER_COMPLETE_NOT_CALLED:
+                    handleFilterCallbacksTimeout();
+                    break;
+            }
+        }
+
+        private void handleFilterCallbacksTimeout() {
+            for (CarrierSmsFilterCallback callback : mFilterAggregator.mCallbacks) {
+                callback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+            }
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 7005fa5..a7e0d8d 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.UserHandle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
@@ -320,7 +321,7 @@
             if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
 
             try {
-                mPhone.getContext().sendBroadcast(signal);
+                mPhone.getContext().sendBroadcastAsUser(signal, UserHandle.ALL);
                 if (DBG) {
                     log("Sending signal " + signal.getAction() + ((signal.getComponent() != null)
                             ? " to the carrier signal receiver: " + signal.getComponent() : ""));
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 8b840de..fa84b20 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -275,7 +275,9 @@
 
                 return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
-                        cellIdentity, maxDataCalls);
+                        cellIdentity, maxDataCalls, false /* isDcNrRestricted */,
+                        false /* isNrAvailable */);
+
             } else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
                 android.hardware.radio.V1_2.DataRegStateResult dataRegState =
                         (android.hardware.radio.V1_2.DataRegStateResult) result;
@@ -290,9 +292,9 @@
 
                 return new NetworkRegistrationState(domain, transportType, regState,
                         accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
-                        cellIdentity, maxDataCalls);
+                        cellIdentity, maxDataCalls, false /* isDcNrRestricted */,
+                        false /* isNrAvailable */);
             }
-
             return null;
         }
 
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 6657d27..8b39e8b 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -25,9 +25,11 @@
 import android.telephony.ClientRequestStats;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
 
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.uicc.IccCardStatus;
 
@@ -37,19 +39,6 @@
  * {@hide}
  */
 public interface CommandsInterface {
-    enum RadioState {
-        RADIO_OFF,         /* Radio explicitly powered off (eg CFUN=0) */
-        RADIO_UNAVAILABLE, /* Radio unavailable (eg, resetting or not booted) */
-        RADIO_ON;          /* Radio is on */
-
-        public boolean isOn() /* and available...*/ {
-            return this == RADIO_ON;
-        }
-
-        public boolean isAvailable() {
-            return this != RADIO_UNAVAILABLE;
-        }
-    }
 
     //***** Constants
 
@@ -121,7 +110,12 @@
     static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM           = 96;
 
     //***** Methods
-    RadioState getRadioState();
+
+    /**
+     * get latest radio power state from modem
+     * @return
+     */
+    @TelephonyManager.RadioPowerState int getRadioState();
 
     /**
      * response.obj.result is an int[2]
@@ -2245,7 +2239,11 @@
      */
     void stopNattKeepalive(int sessionHandle, Message result);
 
-    default public List<ClientRequestStats> getClientRequestStats() {
+    default List<ClientRequestStats> getClientRequestStats() {
         return null;
     }
+
+    default int getIwlanOperationMode() {
+        return TransportManager.IWLAN_OPERATION_MODE_DEFAULT;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 1c65139..05a0081 100755
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -25,7 +25,6 @@
 import android.telephony.ServiceState;
 import android.util.Log;
 
-import java.lang.Override;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -1001,6 +1000,21 @@
     }
 
     /**
+     * Changes the address and presentation for this call.
+     * @param newAddress The new address.
+     * @param numberPresentation The number presentation for the address.
+     */
+    public void setAddress(String newAddress, int numberPresentation) {
+        Rlog.i(TAG, "setAddress = " + newAddress);
+        mAddress = newAddress;
+        mNumberPresentation = numberPresentation;
+    }
+
+    public void setDialString(String newDialString) {
+        mDialString = newDialString;
+    }
+
+    /**
      * Notifies listeners of a change to conference participant(s).
      *
      * @param conferenceParticipants The participant(s).
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index a69865d..c4701f7 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -30,7 +30,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.VoLteServiceState;
+import android.telephony.data.ApnSetting;
 
 import java.util.List;
 
@@ -183,7 +183,8 @@
             if (mRegistry != null) {
                 mRegistry.notifyDataConnectionForSubscriber(subId,
                     PhoneConstantConversions.convertDataState(state),
-                        sender.isDataAllowed(), reason,
+                        sender.isDataAllowed(ApnSetting.getApnTypesBitmaskFromString(apnType)),
+                        reason,
                         sender.getActiveApnHost(apnType),
                         apnType,
                         linkProperties,
@@ -296,10 +297,9 @@
     }
 
     @Override
-    public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState) {
-        // FIXME: subID
+    public void notifySrvccStateChanged(Phone sender, @TelephonyManager.SrvccState int state) {
         try {
-            mRegistry.notifyVoLteServiceStateChanged(lteState);
+            mRegistry.notifySrvccStateChanged(sender.getSubId(), state);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -353,6 +353,15 @@
         }
     }
 
+    @Override
+    public void notifyRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+        try {
+            mRegistry.notifyRadioPowerStateChanged(state);
+        } catch (RemoteException ex) {
+            // system process is dead
+        }
+    }
+
     /**
      * Convert the {@link Phone.DataActivityState} enum into the TelephonyManager.DATA_* constants
      * for the public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 04e96ca..3a96630 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -64,6 +64,7 @@
     private static final int EVENT_POWER_SAVE_MODE_CHANGED      = 3;
     private static final int EVENT_CHARGING_STATE_CHANGED       = 4;
     private static final int EVENT_TETHERING_STATE_CHANGED      = 5;
+    private static final int EVENT_RADIO_AVAILABLE              = 6;
 
     // TODO(b/74006656) load hysteresis values from a property when DeviceStateMonitor starts
     private static final int HYSTERESIS_KBPS = 50;
@@ -200,6 +201,7 @@
         mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
 
         mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
+        mPhone.mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
     }
 
     /**
@@ -344,7 +346,8 @@
         log("handleMessage msg=" + msg, false);
         switch (msg.what) {
             case EVENT_RIL_CONNECTED:
-                onRilConnected();
+            case EVENT_RADIO_AVAILABLE:
+                onReset();
                 break;
             case EVENT_UPDATE_MODE_CHANGED:
                 onSetIndicationUpdateMode(msg.arg1, msg.arg2);
@@ -420,14 +423,14 @@
     }
 
     /**
-     * Called when RIL is connected during boot up or reconnected after modem restart.
+     * Called when RIL is connected during boot up or radio becomes available after modem restart.
      *
      * When modem crashes, if the user turns the screen off before RIL reconnects, device
      * state and filter cannot be sent to modem. Resend the state here so that modem
      * has the correct state (to stop signal strength reporting, etc).
      */
-    private void onRilConnected() {
-        log("RIL connected.", true);
+    private void onReset() {
+        log("onReset.", true);
         sendDeviceState(CHARGING_STATE, mIsCharging);
         sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
         sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
@@ -620,12 +623,10 @@
          * These thresholds are taken from the LTE RSRP defaults in {@link CarrierConfigManager}.
          */
         public static final int[] EUTRAN = new int[] {
-            -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
             -128, /* SIGNAL_STRENGTH_POOR */
             -118, /* SIGNAL_STRENGTH_MODERATE */
             -108, /* SIGNAL_STRENGTH_GOOD */
             -98,  /* SIGNAL_STRENGTH_GREAT */
-            -44   /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
         };
 
         /**
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 025e31c..e282392 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -28,6 +28,7 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
@@ -172,8 +173,9 @@
             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) {
-                mPhone.mDcTracker.setInternalDataEnabled(true);
+            if (mIsInEmergencyCall && mPhone.getDcTracker(TransportType.WWAN) != null) {
+                mPhone.getDcTracker(TransportType.WWAN).setInternalDataEnabled(true);
+
             }
         } else {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
@@ -275,7 +277,9 @@
         clearDisconnected();
 
         // Check for issues which would preclude dialing and throw a CallStateException.
-        checkForDialIssues();
+        boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
+                dialString);
+        checkForDialIssues(isEmergencyCall);
 
         String origNumber = dialString;
         dialString = convertNumberIfNecessary(mPhone, dialString);
@@ -309,8 +313,7 @@
             //we should have failed in !canDial() above before we get here
             throw new CallStateException("cannot dial in current state");
         }
-        boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
-                dialString);
+
         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
                 this, mForegroundCall, isEmergencyCall);
         mHangupPendingMO = false;
@@ -371,7 +374,9 @@
     //CDMA
     public void setIsInEmergencyCall() {
         mIsInEmergencyCall = true;
-        mPhone.mDcTracker.setInternalDataEnabled(false);
+        if (mPhone.getDcTracker(TransportType.WWAN) != null) {
+            mPhone.getDcTracker(TransportType.WWAN).setInternalDataEnabled(false);
+        }
         mPhone.notifyEmergencyCallRegistrants(true);
         mPhone.sendEmergencyCallStateChange(true);
     }
@@ -384,8 +389,11 @@
         // note that this triggers call state changed notif
         clearDisconnected();
 
+        boolean isEmergencyCall =
+                PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
+
         // Check for issues which would preclude dialing and throw a CallStateException.
-        checkForDialIssues();
+        checkForDialIssues(isEmergencyCall);
 
         TelephonyManager tm =
                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
@@ -407,8 +415,6 @@
         }
 
         boolean isPhoneInEcmMode = mPhone.isInEcm();
-        boolean isEmergencyCall =
-                PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
 
         // Cancel Ecm timer if a second emergency call is originating in Ecm mode
         if (isPhoneInEcmMode && isEmergencyCall) {
@@ -476,7 +482,7 @@
             // Some networks need an empty flash before sending the normal one
             CarrierConfigManager configManager = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle bundle = configManager.getConfig();
+            PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
             if (bundle != null) {
                 m3WayCallFlashDelay =
                         bundle.getInt(CarrierConfigManager.KEY_CDMA_3WAYCALL_FLASH_DELAY_INT);
@@ -616,11 +622,11 @@
      * {@link CallStateException} if there is an issue.
      * @throws CallStateException
      */
-    public void checkForDialIssues() throws CallStateException {
+    public void checkForDialIssues(boolean isEmergencyCall) throws CallStateException {
         String disableCall = SystemProperties.get(
                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
 
-        if (!mCi.getRadioState().isOn()) {
+        if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_ON) {
             throw new CallStateException(CallStateException.ERROR_POWER_OFF,
                     "Modem not powered");
         }
@@ -651,6 +657,10 @@
             throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
                     "There is already a foreground and background call.");
         }
+        if (!isEmergencyCall && isInOtaspCall()) {
+            throw new CallStateException(CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS,
+                    "OTASP provisioning is in process.");
+        }
     }
 
     public boolean canTransfer() {
@@ -1622,7 +1632,9 @@
             }
             if (!inEcm) {
                 // Re-initiate data connection
-                mPhone.mDcTracker.setInternalDataEnabled(true);
+                if (mPhone.getDcTracker(TransportType.WWAN) != null) {
+                    mPhone.getDcTracker(TransportType.WWAN).setInternalDataEnabled(true);
+                }
                 mPhone.notifyEmergencyCallRegistrants(false);
             }
             mPhone.sendEmergencyCallStateChange(false);
@@ -1672,6 +1684,18 @@
         return mIsInEmergencyCall;
     }
 
+    /**
+     * @return {@code true} if the pending outgoing call or active call is an OTASP call,
+     * {@code false} otherwise.
+     */
+    public boolean isInOtaspCall() {
+        return mPendingMO != null && mPendingMO.isOtaspCall()
+                || (mForegroundCall.getConnections().stream()
+                .filter(connection -> ((connection instanceof GsmCdmaConnection)
+                        && (((GsmCdmaConnection) connection).isOtaspCall())))
+                .count() > 0);
+    }
+
     private boolean isPhoneTypeGsm() {
         return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
     }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index 14f5aae..32587ec 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -44,6 +44,8 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
+    public static final String OTASP_NUMBER = "*22899";
+
     //***** Instance Variables
 
     GsmCdmaCallTracker mOwner;
@@ -1157,4 +1159,11 @@
 
         return false;
     }
+
+    /**
+     * @return {@code true} if this call is an OTASP activation call, {@code false} otherwise.
+     */
+    public boolean isOtaspCall() {
+        return mAddress != null && OTASP_NUMBER.equals(mAddress);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 86d62a2..c98d9b0 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -55,6 +55,7 @@
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.ImsiEncryptionInfo;
@@ -66,6 +67,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.UssdResponse;
+import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -75,6 +77,7 @@
 import com.android.internal.telephony.cdma.CdmaMmiCode;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -169,7 +172,7 @@
     private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>();
     private IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
     // Used for identify the carrier of current subscription
-    private CarrierIdentifier mCarrerIdentifier;
+    private CarrierResolver mCarrierResolver;
 
     private int mPrecisePhoneType;
 
@@ -223,14 +226,18 @@
         // after CarrierActionAgent.
         mCarrierActionAgent = mTelephonyComponentFactory.makeCarrierActionAgent(this);
         mCarrierSignalAgent = mTelephonyComponentFactory.makeCarrierSignalAgent(this);
+        mTransportManager = mTelephonyComponentFactory.makeTransportManager(this);
         mSST = mTelephonyComponentFactory.makeServiceStateTracker(this, this.mCi);
         // DcTracker uses SST so needs to be created after it is instantiated
-        mDcTracker = mTelephonyComponentFactory.makeDcTracker(this);
-        mCarrerIdentifier = mTelephonyComponentFactory.makeCarrierIdentifier(this);
+        for (int transport : mTransportManager.getAvailableTransports()) {
+            mDcTrackers.put(transport, mTelephonyComponentFactory.makeDcTracker(this,
+                    transport));
+        }
+
+        mCarrierResolver = mTelephonyComponentFactory.makeCarrierResolver(this);
 
         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
         mDeviceStateMonitor = mTelephonyComponentFactory.makeDeviceStateMonitor(this);
-        mAccessNetworksManager = mTelephonyComponentFactory.makeAccessNetworksManager(this);
 
         mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null);
         logd("GsmCdmaPhone: constructor: sub = " + mPhoneId);
@@ -261,6 +268,7 @@
         mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
         mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCi.registerForOn(this, EVENT_RADIO_ON, null);
+        mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
         mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
 
         //GSM
@@ -361,6 +369,7 @@
         if (TextUtils.isEmpty(operatorNumeric)) {
             logd("setIsoCountryProperty: clear 'gsm.sim.operator.iso-country'");
             tm.setSimCountryIsoForPhone(mPhoneId, "");
+            SubscriptionController.getInstance().setCountryIso("", getSubId());
         } else {
             String iso = "";
             try {
@@ -371,6 +380,7 @@
 
             logd("setIsoCountryProperty: set 'gsm.sim.operator.iso-country' to iso=" + iso);
             tm.setSimCountryIsoForPhone(mPhoneId, iso);
+            SubscriptionController.getInstance().setCountryIso(iso, getSubId());
         }
     }
 
@@ -396,14 +406,14 @@
         onUpdateIccAvailability();
         mCT.updatePhoneType();
 
-        CommandsInterface.RadioState radioState = mCi.getRadioState();
-        if (radioState.isAvailable()) {
+        int radioState = mCi.getRadioState();
+        if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
             handleRadioAvailable();
-            if (radioState.isOn()) {
+            if (radioState == TelephonyManager.RADIO_POWER_ON) {
                 handleRadioOn();
             }
         }
-        if (!radioState.isAvailable() || !radioState.isOn()) {
+        if (radioState != TelephonyManager.RADIO_POWER_ON) {
             handleRadioOffOrNotAvailable();
         }
     }
@@ -471,6 +481,11 @@
     }
 
     @Override
+    public TransportManager getTransportManager() {
+        return mTransportManager;
+    }
+
+    @Override
     public void updateVoiceMail() {
         if (isPhoneTypeGsm()) {
             int countVoiceMessages = 0;
@@ -516,21 +531,25 @@
 
             ret = PhoneConstants.DataState.DISCONNECTED;
         } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
-            switch (mDcTracker.getState(apnType)) {
-                case CONNECTED:
-                case DISCONNECTING:
-                    if ( mCT.mState != PhoneConstants.State.IDLE
-                            && !mSST.isConcurrentVoiceAndDataAllowed()) {
-                        ret = PhoneConstants.DataState.SUSPENDED;
-                    } else {
-                        ret = PhoneConstants.DataState.CONNECTED;
-                    }
-                    break;
-                case CONNECTING:
-                    ret = PhoneConstants.DataState.CONNECTING;
-                    break;
-                default:
-                    ret = PhoneConstants.DataState.DISCONNECTED;
+            int currentTransport = mTransportManager.getCurrentTransport(
+                    ApnSetting.getApnTypesBitmaskFromString(apnType));
+            if (getDcTracker(currentTransport) != null) {
+                switch (getDcTracker(currentTransport).getState(apnType)) {
+                    case CONNECTED:
+                    case DISCONNECTING:
+                        if (mCT.mState != PhoneConstants.State.IDLE
+                                && !mSST.isConcurrentVoiceAndDataAllowed()) {
+                            ret = PhoneConstants.DataState.SUSPENDED;
+                        } else {
+                            ret = PhoneConstants.DataState.CONNECTED;
+                        }
+                        break;
+                    case CONNECTING:
+                        ret = PhoneConstants.DataState.CONNECTING;
+                        break;
+                    default:
+                        ret = PhoneConstants.DataState.DISCONNECTED;
+                }
             }
         }
 
@@ -542,8 +561,9 @@
     public DataActivityState getDataActivityState() {
         DataActivityState ret = DataActivityState.NONE;
 
-        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE) {
-            switch (mDcTracker.getActivity()) {
+        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE
+                && getDcTracker(TransportType.WWAN) != null) {
+            switch (getDcTracker(TransportType.WWAN).getActivity()) {
                 case DATAIN:
                     ret = DataActivityState.DATAIN;
                 break;
@@ -624,11 +644,16 @@
         intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
         ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
-        if (DBG) logd("sendEmergencyCallbackModeChange");
+        logi("sendEmergencyCallbackModeChange");
     }
 
     @Override
     public void sendEmergencyCallStateChange(boolean callActive) {
+        if (!isPhoneTypeCdma()) {
+            // It possible that this method got called from ImsPhoneCallTracker#
+            logi("sendEmergencyCallbackModeChange - skip for non-cdma");
+            return;
+        }
         if (mBroadcastEmergencyCallStateChanges) {
             Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
             intent.putExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, callActive);
@@ -1401,7 +1426,7 @@
         if (TextUtils.isEmpty(number)) {
             CarrierConfigManager configManager = (CarrierConfigManager)
                     getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configManager.getConfig();
+            PersistableBundle b = configManager.getConfigForSubId(getSubId());
             if (b != null) {
                 String defaultVmNumber =
                         b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING);
@@ -1409,7 +1434,7 @@
                         b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_ROAMING_STRING);
                 if (!TextUtils.isEmpty(defaultVmNumberRoaming) && mSST.mSS.getRoaming()) {
                     number = defaultVmNumberRoaming;
-                } else {
+                } else if (!TextUtils.isEmpty(defaultVmNumber)) {
                     number = defaultVmNumber;
                 }
             }
@@ -1419,7 +1444,7 @@
             // Read platform settings for dynamic voicemail number
             CarrierConfigManager configManager = (CarrierConfigManager)
                     getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configManager.getConfig();
+            PersistableBundle b = configManager.getConfigForSubId(getSubId());
             if (b != null && b.getBoolean(
                     CarrierConfigManager.KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL)) {
                 number = getLine1Number();
@@ -1554,17 +1579,37 @@
 
     @Override
     public int getCarrierId() {
-        return mCarrerIdentifier.getCarrierId();
+        return mCarrierResolver.getCarrierId();
     }
 
     @Override
     public String getCarrierName() {
-        return mCarrerIdentifier.getCarrierName();
+        return mCarrierResolver.getCarrierName();
+    }
+
+    @Override
+    public int getMNOCarrierId() {
+        return mCarrierResolver.getMnoCarrierId();
+    }
+
+    @Override
+    public int getPreciseCarrierId() {
+        return mCarrierResolver.getPreciseCarrierId();
+    }
+
+    @Override
+    public String getPreciseCarrierName() {
+        return mCarrierResolver.getPreciseCarrierName();
+    }
+
+    @Override
+    public void resolveSubscriptionCarrierId(String simState) {
+        mCarrierResolver.resolveSubscriptionCarrierId(simState);
     }
 
     @Override
     public int getCarrierIdListVersion() {
-        return mCarrerIdentifier.getCarrierListVersion();
+        return mCarrierResolver.getCarrierListVersion();
     }
 
     @Override
@@ -2004,12 +2049,17 @@
 
     @Override
     public boolean getDataRoamingEnabled() {
-        return mDcTracker.getDataRoamingEnabled();
+        if (getDcTracker(TransportType.WWAN) != null) {
+            return getDcTracker(TransportType.WWAN).getDataRoamingEnabled();
+        }
+        return false;
     }
 
     @Override
     public void setDataRoamingEnabled(boolean enable) {
-        mDcTracker.setDataRoamingEnabledByUser(enable);
+        if (getDcTracker(TransportType.WWAN) != null) {
+            getDcTracker(TransportType.WWAN).setDataRoamingEnabledByUser(enable);
+        }
     }
 
     @Override
@@ -2054,17 +2104,25 @@
 
     @Override
     public boolean isUserDataEnabled() {
-        return mDcTracker.isUserDataEnabled();
+        if (getDcTracker(TransportType.WWAN) != null) {
+            return getDcTracker(TransportType.WWAN).isUserDataEnabled();
+        }
+        return false;
     }
 
     @Override
     public boolean isDataEnabled() {
-        return mDcTracker.isDataEnabled();
+        if (getDcTracker(TransportType.WWAN) != null) {
+            return getDcTracker(TransportType.WWAN).isDataEnabled();
+        }
+        return false;
     }
 
     @Override
     public void setUserDataEnabled(boolean enable) {
-        mDcTracker.setUserDataEnabled(enable);
+        if (getDcTracker(TransportType.WWAN) != null) {
+            getDcTracker(TransportType.WWAN).setUserDataEnabled(enable);
+        }
     }
 
     /**
@@ -2101,7 +2159,7 @@
     public boolean supports3gppCallForwardingWhileRoaming() {
         CarrierConfigManager configManager = (CarrierConfigManager)
                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle b = configManager.getConfig();
+        PersistableBundle b = configManager.getConfigForSubId(getSubId());
         if (b != null) {
             return b.getBoolean(
                     CarrierConfigManager.KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
@@ -2221,6 +2279,11 @@
         mRadioOffOrNotAvailableRegistrants.notifyRegistrants();
     }
 
+    private void handleRadioPowerStateChange() {
+        Rlog.d(LOG_TAG, "handleRadioPowerStateChange, state= " + mCi.getRadioState());
+        mNotifier.notifyRadioPowerStateChanged(mCi.getRadioState());
+    }
+
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
@@ -2474,6 +2537,12 @@
                 break;
             }
 
+            case EVENT_RADIO_STATE_CHANGED: {
+                logd("EVENT EVENT_RADIO_STATE_CHANGED");
+                handleRadioPowerStateChange();
+                break;
+            }
+
             case EVENT_SSN:
                 logd("Event EVENT_SSN Received");
                 if (isPhoneTypeGsm()) {
@@ -3022,7 +3091,9 @@
             // send an Intent
             sendEmergencyCallbackModeChange();
             // Re-initiate data connection
-            mDcTracker.setInternalDataEnabled(true);
+            if (getDcTracker(TransportType.WWAN) != null) {
+                getDcTracker(TransportType.WWAN).setInternalDataEnabled(true);
+            }
             notifyEmergencyCallRegistrants(false);
         }
     }
@@ -3372,7 +3443,7 @@
 
         boolean oldPowerState = false; // old power state to off
         if (mResetModemOnRadioTechnologyChange) {
-            if (mCi.getRadioState().isOn()) {
+            if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
                 oldPowerState = true;
                 logd("phoneObjectUpdater: Setting Radio Power to Off");
                 mCi.setRadioPower(false, null);
diff --git a/src/java/com/android/internal/telephony/HalVersion.java b/src/java/com/android/internal/telephony/HalVersion.java
new file mode 100644
index 0000000..c05179b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/HalVersion.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import java.util.Objects;
+
+/**
+ * Represent the version of underlying vendor HAL service.
+ * @see <a href="https://source.android.com/devices/architecture/hidl/versioning">
+ * HIDL versioning</a>.
+ */
+public class HalVersion implements Comparable<HalVersion> {
+    public final int major;
+
+    public final int minor;
+
+    public HalVersion(int major, int minor) {
+        this.major = major;
+        this.minor = minor;
+    }
+
+    @Override
+    public int compareTo(HalVersion ver) {
+        if (ver == null) {
+            return 1;
+        }
+        if (this.major > ver.major) {
+            return 1;
+        } else if (this.major < ver.major) {
+            return -1;
+        } else if (this.minor > ver.minor) {
+            return 1;
+        } else if (this.minor < ver.minor) {
+            return -1;
+        }
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(major, minor);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return ((o instanceof HalVersion) && (o == this || compareTo((HalVersion) o) == 0));
+    }
+
+    /**
+     * @return True if the version is greater than the compared version.
+     */
+    public boolean greater(HalVersion ver) {
+        return compareTo(ver) > 0;
+    }
+
+    /**
+     * @return True if the version is less than the compared version.
+     */
+    public boolean less(HalVersion ver) {
+        return compareTo(ver) < 0;
+    }
+
+    /**
+     * @return True if the version is greater than or equal to the compared version.
+     */
+    public boolean greaterOrEqual(HalVersion ver) {
+        return greater(ver) || equals(ver);
+    }
+
+    /**
+     * @return True if the version is less than or equal to the compared version.
+     */
+    public boolean lessOrEqual(HalVersion ver) {
+        return less(ver) || equals(ver);
+    }
+
+    @Override
+    public String toString() {
+        return major + "." + minor;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 8feec94..76af170 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -294,7 +294,7 @@
         String[] emails = null;
         String pin2 = null;
 
-        String[] tokens = where.split("AND");
+        String[] tokens = where.split(" AND ");
         int n = tokens.length;
 
         while (--n >= 0) {
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 91c0040..70db301 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -26,7 +26,6 @@
 import android.telephony.ServiceState;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsSmsListener;
-import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase;
@@ -37,6 +36,7 @@
 import com.android.ims.ImsManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.util.SMSDispatcherUtil;
 
 import java.util.HashMap;
@@ -62,12 +62,15 @@
     private volatile boolean mIsImsServiceUp;
     private volatile boolean mIsRegistered;
     private final ImsManager.Connector mImsManagerConnector;
+    /** Telephony metrics instance for logging metrics event */
+    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
+
     /**
      * Listen to the IMS service state change
      *
      */
-    private ImsRegistrationImplBase.Callback mRegistrationCallback =
-            new ImsRegistrationImplBase.Callback() {
+    private android.telephony.ims.ImsMmTelManager.RegistrationCallback mRegistrationCallback =
+            new android.telephony.ims.ImsMmTelManager.RegistrationCallback() {
                 @Override
                 public void onRegistered(
                         @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
@@ -95,12 +98,13 @@
                 }
             };
 
-    private ImsFeature.CapabilityCallback mCapabilityCallback =
-            new ImsFeature.CapabilityCallback() {
+    private android.telephony.ims.ImsMmTelManager.CapabilityCallback mCapabilityCallback =
+            new android.telephony.ims.ImsMmTelManager.CapabilityCallback() {
                 @Override
-                public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+                public void onCapabilitiesStatusChanged(
+                        MmTelFeature.MmTelCapabilities capabilities) {
                     synchronized (mLock) {
-                        mIsSmsCapable = config.isCapable(
+                        mIsSmsCapable = capabilities.isCapable(
                                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS);
                     }
                 }
@@ -112,6 +116,7 @@
                 int reason) throws RemoteException {
             Rlog.d(TAG, "onSendSmsResult token=" + token + " messageRef=" + messageRef
                     + " status=" + status + " reason=" + reason);
+            mMetrics.writeOnImsServiceSmsSolicitedResponse(mPhone.getPhoneId(), status, reason);
             SmsTracker tracker = mTrackers.get(token);
             if (tracker == null) {
                 throw new IllegalArgumentException("Invalid token.");
@@ -165,8 +170,7 @@
         }
 
         @Override
-        public void onSmsReceived(int token, String format, byte[] pdu)
-                throws RemoteException {
+        public void onSmsReceived(int token, String format, byte[] pdu) {
             Rlog.d(TAG, "SMS received.");
             android.telephony.SmsMessage message =
                     android.telephony.SmsMessage.createFromPdu(pdu, format);
@@ -199,6 +203,7 @@
                     Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
                 }
             }, true);
+            mMetrics.writeImsServiceNewSms(mPhone.getPhoneId(), format);
         }
     };
 
@@ -339,8 +344,9 @@
         byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
         byte smsc[] = (byte[]) map.get(MAP_KEY_SMSC);
         boolean isRetry = tracker.mRetryCount > 0;
+        String format = getFormat();
 
-        if (SmsConstants.FORMAT_3GPP.equals(getFormat()) && tracker.mRetryCount > 0) {
+        if (SmsConstants.FORMAT_3GPP.equals(format) && tracker.mRetryCount > 0) {
             // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
             //   TP-RD (bit 2) is 1 for retry
             //   and TP-MR is set to previously failed sms TP-MR
@@ -356,10 +362,11 @@
             getImsManager().sendSms(
                     token,
                     tracker.mMessageRef,
-                    getFormat(),
+                    format,
                     smsc != null ? new String(smsc) : null,
                     isRetry,
                     pdu);
+            mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format);
         } catch (ImsException e) {
             Rlog.e(TAG, "sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
             fallbackToPstn(token, tracker);
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index b69bae2..d582762 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -89,6 +89,8 @@
 
     private final Phone mPhone;
 
+    private final NitzStateMachine mNitzStateMachine;
+
     /** SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX */
     private int mSimState;
 
@@ -138,7 +140,7 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_REQUEST_CELL_INFO:
-                mPhone.getAllCellInfo(null, obtainMessage(EVENT_RESPONSE_CELL_INFO));
+                mPhone.requestCellInfoUpdate(null, obtainMessage(EVENT_RESPONSE_CELL_INFO));
                 break;
 
             case EVENT_UNSOL_CELL_INFO:
@@ -172,11 +174,13 @@
      * Constructor
      *
      * @param phone The phone object
+     * @param nitzStateMachine NITZ state machine
      * @param looper The looper message handler
      */
-    public LocaleTracker(Phone phone, Looper looper)  {
+    public LocaleTracker(Phone phone, NitzStateMachine nitzStateMachine, Looper looper)  {
         super(looper);
         mPhone = phone;
+        mNitzStateMachine = nitzStateMachine;
         mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
         final IntentFilter filter = new IntentFilter();
@@ -391,6 +395,7 @@
         String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso;
         log(msg);
         mLocalLog.log(msg);
+        boolean countryChanged = false;
         if (!Objects.equals(countryIso, mCurrentCountryIso)) {
             msg = "updateLocale: Change the current country to " + countryIso;
             log(msg);
@@ -405,6 +410,13 @@
             // broadcast on forbidden channels.
             ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE))
                     .setCountryCode(countryIso);
+            countryChanged = true;
+        }
+
+        if (TextUtils.isEmpty(countryIso)) {
+            mNitzStateMachine.handleNetworkCountryCodeUnavailable();
+        } else {
+            mNitzStateMachine.handleNetworkCountryCodeSet(countryChanged);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index fb28194..b1cfb7b 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -29,7 +29,7 @@
 import com.android.internal.app.LocaleStore.LocaleInfo;
 
 import libcore.icu.ICU;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index ae7ede0..04d09a5 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -127,7 +127,8 @@
     private class NetworkServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            logd("service connected.");
+            logd("service " + name + " for transport "
+                    + TransportType.toString(mTransportType) + " is now connected.");
             mServiceBinder = (INetworkService.Stub) service;
             mDeathRecipient = new RegManagerDeathRecipient(name);
             try {
@@ -144,7 +145,8 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            logd("onServiceDisconnected " + name);
+            logd("service " + name + " for transport "
+                    + TransportType.toString(mTransportType) + " is now disconnected.");
             if (mServiceBinder != null) {
                 mServiceBinder.unlinkToDeath(mDeathRecipient, 0);
             }
@@ -180,6 +182,8 @@
         try {
             // We bind this as a foreground service because it is operating directly on the SIM,
             // and we do not want it subjected to power-savings restrictions while doing so.
+            logd("Trying to bind " + getPackageName() + " for transport "
+                    + TransportType.toString(mTransportType));
             return mPhone.getContext().bindService(intent, new NetworkServiceConnection(),
                     Context.BIND_AUTO_CREATE);
         } catch (SecurityException e) {
@@ -219,9 +223,6 @@
             packageName = b.getString(carrierConfig, packageName);
         }
 
-        logd("Binding to packageName " + packageName + " for transport type"
-                + mTransportType);
-
         return packageName;
     }
 
diff --git a/src/java/com/android/internal/telephony/NewNitzStateMachine.java b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
index 20c729f..74d9862 100644
--- a/src/java/com/android/internal/telephony/NewNitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
@@ -72,10 +72,10 @@
     /**
      * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
      * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
-     * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
-     * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
-     * less reliable algorithms than NITZ-based detection such as by just using network country
-     * code.
+     * and {@link #handleNetworkCountryCodeUnavailable()}. The flag can be used when historic NITZ
+     * data may no longer be valid. {@code false} indicates it is reasonable to try to set the time
+     * zone using less reliable algorithms than NITZ-based detection such as by just using network
+     * country code.
      */
     private boolean mNitzTimeZoneDetectionSuccessful = false;
 
@@ -282,9 +282,9 @@
     }
 
     @Override
-    public void handleNetworkUnavailable() {
+    public void handleNetworkCountryCodeUnavailable() {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+            Rlog.d(LOG_TAG, "handleNetworkCountryCodeUnavailable");
         }
 
         mGotCountryCode = false;
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index 6a5e47a..9b91573 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -48,9 +48,10 @@
     void handleNetworkAvailable();
 
     /**
-     * Informs the {@link NitzStateMachine} that the network has become unavailable.
+     * Informs the {@link NitzStateMachine} that the country code from network has become
+     * unavailable.
      */
-    void handleNetworkUnavailable();
+    void handleNetworkCountryCodeUnavailable();
 
     /**
      * Handle a new NITZ signal being received.
diff --git a/src/java/com/android/internal/telephony/OldNitzStateMachine.java b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
index bb43f1e..916dc65 100644
--- a/src/java/com/android/internal/telephony/OldNitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
@@ -73,10 +73,10 @@
     /**
      * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
      * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
-     * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
-     * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
-     * less reliable algorithms than NITZ-based detection such as by just using network country
-     * code.
+     * and {@link #handleNetworkCountryCodeUnavailable()}. The flag can be used when historic NITZ
+     * data may no longer be valid. {@code false} indicates it is reasonable to try to set the time
+     * zone using less reliable algorithms than NITZ-based detection such as by just using network
+     * country code.
      */
     private boolean mNitzTimeZoneDetectionSuccessful = false;
 
@@ -290,9 +290,9 @@
     }
 
     @Override
-    public void handleNetworkUnavailable() {
+    public void handleNetworkCountryCodeUnavailable() {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+            Rlog.d(LOG_TAG, "handleNetworkCountryCodeUnavailable");
         }
 
         mGotCountryCode = false;
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index f09b386..c4f203e 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +41,7 @@
 import android.provider.Settings;
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
@@ -53,17 +55,19 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.VoLteServiceState;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
+import android.util.SparseArray;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
 import com.android.internal.R;
-import com.android.internal.telephony.dataconnection.AccessNetworksManager;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -78,6 +82,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -161,6 +166,7 @@
     protected static final int EVENT_GET_CALL_FORWARD_DONE       = 13;
     protected static final int EVENT_CALL_RING                   = 14;
     private static final int EVENT_CALL_RING_CONTINUE            = 15;
+    private static final int EVENT_ALL_DATA_DISCONNECTED         = 16;
 
     // Used to intercept the carrier selection calls so that
     // we can save the values.
@@ -199,8 +205,10 @@
     protected static final int EVENT_SET_ROAMING_PREFERENCE_DONE    = 44;
     protected static final int EVENT_MODEM_RESET                    = 45;
     protected static final int EVENT_VRS_OR_RAT_CHANGED             = 46;
+    // Radio state change
+    protected static final int EVENT_RADIO_STATE_CHANGED            = 47;
 
-    protected static final int EVENT_LAST                       = EVENT_VRS_OR_RAT_CHANGED;
+    protected static final int EVENT_LAST                       = EVENT_RADIO_STATE_CHANGED;
 
     // For shared prefs.
     private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -250,7 +258,11 @@
     public CommandsInterface mCi;
     protected int mVmCount = 0;
     private boolean mDnsCheckDisabled;
-    public DcTracker mDcTracker;
+    // 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<>();
     /* Used for dispatching signals to configured carrier apps */
     protected CarrierSignalAgent mCarrierSignalAgent;
     /* Used for dispatching carrier action from carrier apps */
@@ -279,7 +291,7 @@
     private final String mActionDetached;
     private final String mActionAttached;
     protected DeviceStateMonitor mDeviceStateMonitor;
-    protected AccessNetworksManager mAccessNetworksManager;
+    protected TransportManager mTransportManager;
 
     protected int mPhoneId;
 
@@ -306,47 +318,35 @@
     public static final String EXTRA_KEY_ALERT_SHOW = "alertShow";
     public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage";
 
-    private final RegistrantList mPreciseCallStateRegistrants
-            = new RegistrantList();
+    private final RegistrantList mPreciseCallStateRegistrants = new RegistrantList();
 
-    private final RegistrantList mHandoverRegistrants
-            = new RegistrantList();
+    private final RegistrantList mHandoverRegistrants = new RegistrantList();
 
-    private final RegistrantList mNewRingingConnectionRegistrants
-            = new RegistrantList();
+    private final RegistrantList mNewRingingConnectionRegistrants = new RegistrantList();
 
-    private final RegistrantList mIncomingRingRegistrants
-            = new RegistrantList();
+    private final RegistrantList mIncomingRingRegistrants = new RegistrantList();
 
-    protected final RegistrantList mDisconnectRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mDisconnectRegistrants = new RegistrantList();
 
-    private final RegistrantList mServiceStateRegistrants
-            = new RegistrantList();
+    private final RegistrantList mServiceStateRegistrants = new RegistrantList();
 
-    protected final RegistrantList mMmiCompleteRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mMmiCompleteRegistrants = new RegistrantList();
 
-    protected final RegistrantList mMmiRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mMmiRegistrants = new RegistrantList();
 
-    protected final RegistrantList mUnknownConnectionRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mUnknownConnectionRegistrants = new RegistrantList();
 
-    protected final RegistrantList mSuppServiceFailedRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mSuppServiceFailedRegistrants = new RegistrantList();
 
-    protected final RegistrantList mRadioOffOrNotAvailableRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mRadioOffOrNotAvailableRegistrants = new RegistrantList();
 
-    protected final RegistrantList mSimRecordsLoadedRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mSimRecordsLoadedRegistrants = new RegistrantList();
 
-    private final RegistrantList mVideoCapabilityChangedRegistrants
-            = new RegistrantList();
+    private final RegistrantList mVideoCapabilityChangedRegistrants = new RegistrantList();
 
-    protected final RegistrantList mEmergencyCallToggledRegistrants
-            = new RegistrantList();
+    protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList();
+
+    private final RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
 
     private final RegistrantList mCellInfoRegistrants = new RegistrantList();
 
@@ -603,7 +603,7 @@
     public boolean supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming() {
         CarrierConfigManager configManager = (CarrierConfigManager)
                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle b = configManager.getConfig();
+        PersistableBundle b = configManager.getConfigForSubId(getSubId());
         if (b != null) {
             return b.getBoolean(
                     CarrierConfigManager
@@ -712,6 +712,12 @@
                 onCheckForNetworkSelectionModeAutomatic(msg);
                 break;
             }
+
+            case EVENT_ALL_DATA_DISCONNECTED:
+                if (areAllDataDisconnected()) {
+                    mAllDataDisconnectedRegistrants.notifyRegistrants();
+                }
+                break;
             default:
                 throw new RuntimeException("unexpected event not handled");
         }
@@ -739,7 +745,7 @@
         if (ret != null && ret.length != 0) {
             int state = ret[0];
             switch(state) {
-                case VoLteServiceState.HANDOVER_STARTED:
+                case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
                     srvccState = Call.SrvccState.STARTED;
                     if (imsPhone != null) {
                         conn = imsPhone.getHandoverConnection();
@@ -748,7 +754,7 @@
                         Rlog.d(LOG_TAG, "HANDOVER_STARTED: mImsPhone null");
                     }
                     break;
-                case VoLteServiceState.HANDOVER_COMPLETED:
+                case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
                     srvccState = Call.SrvccState.COMPLETED;
                     if (imsPhone != null) {
                         imsPhone.notifySrvccState(srvccState);
@@ -756,8 +762,8 @@
                         Rlog.d(LOG_TAG, "HANDOVER_COMPLETED: mImsPhone null");
                     }
                     break;
-                case VoLteServiceState.HANDOVER_FAILED:
-                case VoLteServiceState.HANDOVER_CANCELED:
+                case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
+                case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
                     srvccState = Call.SrvccState.FAILED;
                     break;
 
@@ -768,8 +774,7 @@
 
             getCallTracker().notifySrvccState(srvccState, conn);
 
-            VoLteServiceState lteState = new VoLteServiceState(state);
-            notifyVoLteServiceStateChanged(lteState);
+            notifySrvccStateChanged(state);
         }
     }
 
@@ -1631,6 +1636,13 @@
     }
 
     /**
+     * @return The instance of transport manager
+     */
+    public TransportManager getTransportManager() {
+        return null;
+    }
+
+    /**
      * Update voice activation state
      */
     public void setVoiceActivationState(int state) {
@@ -1721,7 +1733,7 @@
      * @param workSource calling WorkSource
      * @param rspMsg the response message containing the cell info
      */
-    public void getAllCellInfo(WorkSource workSource, Message rspMsg) {
+    public void requestCellInfoUpdate(WorkSource workSource, Message rspMsg) {
         getServiceStateTracker().requestAllCellInfo(workSource, rspMsg);
     }
 
@@ -2138,15 +2150,24 @@
     }
 
     /**
-     * Perform the specified type of NV config reset. The radio will be taken offline
-     * and the device must be rebooted after erasing the NV. Used for device
+     * Perform the radio modem reboot. The radio will be taken offline. Used for device
      * configuration by some CDMA operators.
+     * TODO: reuse nvResetConfig for now, should move to separate HAL API.
      *
-     * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @param response Callback message.
      */
-    public void nvResetConfig(int resetType, Message response) {
-        mCi.nvResetConfig(resetType, response);
+    public void rebootModem(Message response) {
+        mCi.nvResetConfig(1 /* 1: reload NV reset, trigger a modem reboot */, response);
+    }
+
+    /**
+     * Perform the modem configuration reset. Used for device configuration by some CDMA operators.
+     * TODO: reuse nvResetConfig for now, should move to separate HAL API.
+     *
+     * @param response Callback message.
+     */
+    public void resetModemConfig(Message response) {
+        mCi.nvResetConfig(3 /* factory NV reset */, response);
     }
 
     public void notifyDataActivity() {
@@ -2173,8 +2194,11 @@
 
     public void notifyDataConnection(String reason) {
         String types[] = getActiveApnTypes();
-        for (String apnType : types) {
-            mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
+        if (types != null) {
+            for (String apnType : types) {
+                mNotifier.notifyDataConnection(this, reason, apnType,
+                        getDataConnectionState(apnType));
+            }
         }
     }
 
@@ -2210,8 +2234,11 @@
         mNotifier.notifyPhysicalChannelConfiguration(this, configs);
     }
 
-    public void notifyVoLteServiceStateChanged(VoLteServiceState lteState) {
-        mNotifier.notifyVoLteServiceStateChanged(this, lteState);
+    /**
+     * Notify listeners that SRVCC state has changed.
+     */
+    public void notifySrvccStateChanged(int state) {
+        mNotifier.notifySrvccStateChanged(this, state);
     }
 
     /**
@@ -2316,6 +2343,15 @@
         } else {
             Rlog.e(LOG_TAG, "setVoiceMessageCount in sharedPreference: invalid subId " + subId);
         }
+        // store voice mail count in SIM
+        IccRecords records = UiccController.getInstance().getIccRecords(
+                mPhoneId, UiccController.APP_FAM_3GPP);
+        if (records != null) {
+            Rlog.d(LOG_TAG, "setVoiceMessageCount: updating SIM Records");
+            records.setVoiceMessageWaiting(1, countWaiting);
+        } else {
+            Rlog.d(LOG_TAG, "setVoiceMessageCount: SIM Records not found");
+        }
         // notify listeners of voice mail
         notifyMessageWaitingIndicator();
     }
@@ -2792,24 +2828,35 @@
     /**
      * Returns an array of string identifiers for the APN types serviced by the
      * currently active.
-     *  @return The string array will always return at least one entry, Phone.APN_TYPE_DEFAULT.
-     * TODO: Revisit if we always should return at least one entry.
+     *
+     * @return The string array of APN types. Return null if no active APN types.
      */
+    @Nullable
     public String[] getActiveApnTypes() {
-        if (mDcTracker == null) {
-            return null;
+        if (mTransportManager != null) {
+            List<String> typesList = new ArrayList<>();
+            for (int transportType : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transportType) != null) {
+                    typesList.addAll(Arrays.asList(
+                            getDcTracker(transportType).getActiveApnTypes()));
+                }
+            }
+
+            return typesList.toArray(new String[typesList.size()]);
         }
 
-        return mDcTracker.getActiveApnTypes();
+        return null;
     }
 
     /**
-     * Check if TETHER_DUN_APN setting or config_tether_apndata includes APN that matches
-     * current operator.
+     * Check if there are matching tethering (i.e DUN) for the carrier.
      * @return true if there is a matching DUN APN.
      */
     public boolean hasMatchedTetherApnSetting() {
-        return mDcTracker.hasMatchedTetherApnSetting();
+        if (getDcTracker(TransportType.WWAN) != null) {
+            return getDcTracker(TransportType.WWAN).hasMatchedTetherApnSetting();
+        }
+        return false;
     }
 
     /**
@@ -2817,40 +2864,71 @@
      *  @return type as a string or null if none.
      */
     public String getActiveApnHost(String apnType) {
-        return mDcTracker.getActiveApnString(apnType);
+        if (mTransportManager != null) {
+            int transportType = mTransportManager.getCurrentTransport(
+                    ApnSetting.getApnTypesBitmaskFromString(apnType));
+            if (getDcTracker(transportType) != null) {
+                return getDcTracker(transportType).getActiveApnString(apnType);
+            }
+        }
+
+        return null;
     }
 
     /**
      * Return the LinkProperties for the named apn or null if not available
      */
     public LinkProperties getLinkProperties(String apnType) {
-        return mDcTracker.getLinkProperties(apnType);
+        if (mTransportManager != null) {
+            int transport = mTransportManager.getCurrentTransport(
+                    ApnSetting.getApnTypesBitmaskFromString(apnType));
+            if (getDcTracker(transport) != null) {
+                return getDcTracker(transport).getLinkProperties(apnType);
+            }
+        }
+        return null;
     }
 
     /**
      * Return the NetworkCapabilities
      */
     public NetworkCapabilities getNetworkCapabilities(String apnType) {
-        return mDcTracker.getNetworkCapabilities(apnType);
+        if (mTransportManager != null) {
+            int transportType = mTransportManager.getCurrentTransport(
+                    ApnSetting.getApnTypesBitmaskFromString(apnType));
+            if (getDcTracker(transportType) != null) {
+                return getDcTracker(transportType).getNetworkCapabilities(apnType);
+            }
+        }
+        return null;
     }
 
     /**
-     * Report on whether data connectivity is allowed.
+     * Report on whether data connectivity is allowed for given APN type.
+     *
+     * @param apnType APN type
      *
      * @return True if data is allowed to be established.
      */
-    public boolean isDataAllowed() {
-        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(null)));
+    public boolean isDataAllowed(@ApnType int apnType) {
+        return isDataAllowed(apnType, null);
     }
 
     /**
      * 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(DataConnectionReasons reasons) {
-        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(reasons)));
+    public boolean isDataAllowed(@ApnType int apnType, DataConnectionReasons reasons) {
+        if (mTransportManager != null) {
+            int transport = mTransportManager.getCurrentTransport(apnType);
+            if (getDcTracker(transport) != null) {
+                return getDcTracker(transport).isDataAllowed(reasons);
+            }
+        }
+        return false;
     }
 
 
@@ -3040,7 +3118,15 @@
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      */
     public String[] getPcscfAddress(String apnType) {
-        return mDcTracker.getPcscfAddress(apnType);
+        if (mTransportManager != null) {
+            int transportType = mTransportManager.getCurrentTransport(
+                    ApnSetting.getApnTypesBitmaskFromString(apnType));
+            if (getDcTracker(transportType) != null) {
+                return getDcTracker(transportType).getPcscfAddress(apnType);
+            }
+        }
+
+        return null;
     }
 
     /**
@@ -3086,10 +3172,25 @@
         return null;
     }
 
+    public int getMNOCarrierId() {
+        return TelephonyManager.UNKNOWN_CARRIER_ID;
+    }
+
+    public int getPreciseCarrierId() {
+        return TelephonyManager.UNKNOWN_CARRIER_ID;
+    }
+
+    public String getPreciseCarrierName() {
+        return null;
+    }
+
     public int getCarrierIdListVersion() {
         return TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
     }
 
+    public void resolveSubscriptionCarrierId(String simState) {
+    }
+
     /**
      *  Resets the Carrier Keys in the database. This involves 2 steps:
      *  1. Delete the keys from the database.
@@ -3276,6 +3377,20 @@
     }
 
     /**
+     * @return true if the IMS capability for the registration technology specified is available,
+     * false otherwise.
+     */
+    public boolean isImsCapabilityAvailable(int capability, int regTech) {
+        Phone imsPhone = mImsPhone;
+        boolean isAvailable = false;
+        if (imsPhone != null) {
+            isAvailable = imsPhone.isImsCapabilityAvailable(capability, regTech);
+        }
+        Rlog.d(LOG_TAG, "isImsRegistered =" + isAvailable);
+        return isAvailable;
+    }
+
+    /**
      * Get Volte Feature Availability
      */
     public boolean isVolteEnabled() {
@@ -3317,17 +3432,24 @@
     }
 
     /**
+     * @return returns the latest radio state from the modem
+     */
+    public int getRadioPowerState() {
+        return mCi.getRadioState();
+    }
+
+    /**
      * Is Radio Present on the device and is it accessible
      */
     public boolean isRadioAvailable() {
-        return mCi.getRadioState().isAvailable();
+        return mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE;
     }
 
     /**
      * Is Radio turned on
      */
     public boolean isRadioOn() {
-        return mCi.getRadioState().isOn();
+        return mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON;
     }
 
     /**
@@ -3543,31 +3665,59 @@
     }
 
     public void updateDataConnectionTracker() {
-        mDcTracker.update();
-    }
-
-    public void setInternalDataEnabled(boolean enable, Message onCompleteMsg) {
-        mDcTracker.setInternalDataEnabled(enable, onCompleteMsg);
+        if (mTransportManager != null) {
+            for (int transport : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transport) != null) {
+                    getDcTracker(transport).update();
+                }
+            }
+        }
     }
 
     public boolean updateCurrentCarrierInProvider() {
         return false;
     }
 
-    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
-        mDcTracker.registerForAllDataDisconnected(h, what, obj);
+    /**
+     * @return True if all data connections are disconnected.
+     */
+    public boolean areAllDataDisconnected() {
+        if (mTransportManager != null) {
+            for (int transport : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transport) != null && !getDcTracker(transport).isDisconnected()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public void registerForAllDataDisconnected(Handler h, int what) {
+        mAllDataDisconnectedRegistrants.addUnique(h, what, null);
+        if (mTransportManager != null) {
+            for (int transport : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transport) != null && !getDcTracker(transport).isDisconnected()) {
+                    getDcTracker(transport).registerForAllDataDisconnected(
+                            this, EVENT_ALL_DATA_DISCONNECTED);
+                }
+            }
+        }
     }
 
     public void unregisterForAllDataDisconnected(Handler h) {
-        mDcTracker.unregisterForAllDataDisconnected(h);
+        mAllDataDisconnectedRegistrants.remove(h);
     }
 
     public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
-        mDcTracker.registerForDataEnabledChanged(h, what, obj);
+        if (getDcTracker(TransportType.WWAN) != null) {
+            getDcTracker(TransportType.WWAN).registerForDataEnabledChanged(h, what, obj);
+        }
     }
 
     public void unregisterForDataEnabledChanged(Handler h) {
-        mDcTracker.unregisterForDataEnabledChanged(h);
+        if (getDcTracker(TransportType.WWAN) != null) {
+            getDcTracker(TransportType.WWAN).unregisterForDataEnabledChanged(h);
+        }
     }
 
     public IccSmsInterfaceManager getIccSmsInterfaceManager(){
@@ -3645,7 +3795,13 @@
      * @param enabled True if enabling the data, otherwise disabling.
      */
     public void setPolicyDataEnabled(boolean enabled) {
-        mDcTracker.setPolicyDataEnabled(enabled);
+        if (mTransportManager != null) {
+            for (int transport : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transport) != null) {
+                    getDcTracker(transport).setPolicyDataEnabled(enabled);
+                }
+            }
+        }
     }
 
     /**
@@ -3682,12 +3838,21 @@
             String gid2, String pnn, String spn) {
     }
 
+    /**
+     * 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);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
         pw.println(" mCi=" + mCi);
         pw.println(" mDnsCheckDisabled=" + mDnsCheckDisabled);
-        pw.println(" mDcTracker=" + mDcTracker);
         pw.println(" mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
         pw.println(" mCallRingContinueToken=" + mCallRingContinueToken);
         pw.println(" mCallRingDelay=" + mCallRingDelay);
@@ -3731,15 +3896,14 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
-        if (mDcTracker != null) {
-            try {
-                mDcTracker.dump(fd, pw, args);
-            } catch (Exception e) {
-                e.printStackTrace();
+        if (mTransportManager != null) {
+            for (int transport : mTransportManager.getAvailableTransports()) {
+                if (getDcTracker(transport) != null) {
+                    getDcTracker(transport).dump(fd, pw, args);
+                    pw.flush();
+                    pw.println("++++++++++++++++++++++++++++++++");
+                }
             }
-
-            pw.flush();
-            pw.println("++++++++++++++++++++++++++++++++");
         }
 
         if (getServiceStateTracker() != null) {
@@ -3803,6 +3967,10 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
+        if (mTransportManager != null) {
+            mTransportManager.dump(fd, pw, args);
+        }
+
         if (mCi != null && mCi instanceof RIL) {
             try {
                 ((RIL)mCi).dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index b5eb9a7..5359941 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -259,8 +259,7 @@
                 sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
                 for (int i = 0; i < numPhones; i++) {
                     sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
-                            sPhoneSwitcher, sc, sSubscriptionMonitor, Looper.myLooper(),
-                            sContext, i, sPhones[i].mDcTracker);
+                            sSubscriptionMonitor, Looper.myLooper(), sPhones[i]);
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 6829322..0568187 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -20,7 +20,7 @@
 import android.telephony.CellLocation;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
-import android.telephony.VoLteServiceState;
+import android.telephony.TelephonyManager;
 
 import java.util.List;
 
@@ -64,7 +64,8 @@
     public void notifyPreciseDataConnectionFailed(Phone sender, String reason, String apnType,
             String apn, String failCause);
 
-    public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState);
+    /** send a notification that the SRVCC state has changed.*/
+    void notifySrvccStateChanged(Phone sender, @TelephonyManager.SrvccState int state);
 
     public void notifyVoiceActivationStateChanged(Phone sender, int activationState);
 
@@ -75,4 +76,6 @@
     public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData);
 
     public void notifyPhoneCapabilityChanged(PhoneCapability capability);
+
+    void notifyRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index bc8fdb5..75dd049 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -66,7 +66,7 @@
     private final static boolean VDBG = false;
 
     private final List<DcRequest> mPrioritizedDcRequests = new ArrayList<DcRequest>();
-    private final RegistrantList[] mActivePhoneRegistrants;
+    private final RegistrantList mActivePhoneRegistrants;
     private final SubscriptionController mSubscriptionController;
     private final int[] mPhoneSubscriptions;
     private final CommandsInterface[] mCommandsInterfaces;
@@ -86,9 +86,9 @@
     // If mPreferredDataSubId is an active subscription, it overrides
     // mDefaultDataSubId and decides:
     // 1. In modem layer, which subscription is preferred to have data traffic on.
-    // 2. In TelephonyNetworkFactory, which subscription will apply default network requets, which
+    // 2. In TelephonyNetworkFactory, which subscription will apply default network requests, which
     //    are requests without specifying a subId.
-    private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private int mPreferredDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
 
     @VisibleForTesting
     // Corresponding phoneId after considerting mPreferredDataSubId and mDefaultDataSubId above.
@@ -99,8 +99,19 @@
     private static final int EVENT_REQUEST_NETWORK                = 103;
     private static final int EVENT_RELEASE_NETWORK                = 104;
     private static final int EVENT_EMERGENCY_TOGGLE               = 105;
-    private static final int EVENT_RESEND_DATA_ALLOWED            = 106;
+    private static final int EVENT_RADIO_CAPABILITY_CHANGED       = 106;
     private static final int EVENT_PREFERRED_SUBSCRIPTION_CHANGED = 107;
+    private static final int EVENT_RADIO_AVAILABLE                = 108;
+
+    // Depending on version of IRadioConfig, we need to send either RIL_REQUEST_ALLOW_DATA if it's
+    // 1.0, or RIL_REQUEST_SET_PREFERRED_DATA if it's 1.1 or later. So internally mHalCommandToUse
+    // will be either HAL_COMMAND_ALLOW_DATA or HAL_COMMAND_ALLOW_DATA or HAL_COMMAND_UNKNOWN.
+    private static final int HAL_COMMAND_UNKNOWN        = 0;
+    private static final int HAL_COMMAND_ALLOW_DATA     = 1;
+    private static final int HAL_COMMAND_PREFERRED_DATA = 2;
+    private int mHalCommandToUse = HAL_COMMAND_UNKNOWN;
+
+    private RadioConfig mRadioConfig;
 
     private final static int MAX_LOCAL_LOG_LINES = 30;
 
@@ -138,6 +149,7 @@
         mLocalLog = null;
         mActivePhoneRegistrants = null;
         mNumPhones = 0;
+        mRadioConfig = RadioConfig.getInstance(mContext);
         mPhoneStateListener = new PhoneStateListener(looper) {
             public void onPhoneCapabilityChanged(PhoneCapability capability) {
                 onPhoneCapabilityChangedInternal(capability);
@@ -158,6 +170,7 @@
         mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES);
 
         mSubscriptionController = subscriptionController;
+        mRadioConfig = RadioConfig.getInstance(mContext);
 
         mPhoneStateListener = new PhoneStateListener(looper) {
             public void onPhoneCapabilityChanged(PhoneCapability capability) {
@@ -169,10 +182,9 @@
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener, LISTEN_PHONE_CAPABILITY_CHANGE);
 
-        mActivePhoneRegistrants = new RegistrantList[numPhones];
+        mActivePhoneRegistrants = new RegistrantList();
         mPhoneStates = new PhoneState[numPhones];
         for (int i = 0; i < numPhones; i++) {
-            mActivePhoneRegistrants[i] = new RegistrantList();
             mPhoneStates[i] = new PhoneState();
             if (mPhones[i] != null) {
                 mPhones[i].registerForEmergencyCallToggle(this, EVENT_EMERGENCY_TOGGLE, null);
@@ -181,6 +193,7 @@
 
         mCommandsInterfaces = cis;
 
+        mCommandsInterfaces[0].registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
         try {
             tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
                     mSubscriptionsChangedListener);
@@ -255,14 +268,19 @@
                 onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
                 break;
             }
-            case EVENT_RESEND_DATA_ALLOWED: {
-                onResendDataAllowed(msg);
+            case EVENT_RADIO_CAPABILITY_CHANGED: {
+                resendRilCommands(msg);
                 break;
             }
             case EVENT_PREFERRED_SUBSCRIPTION_CHANGED: {
                 onEvaluate(REQUESTS_UNCHANGED, "preferredDataSubIdChanged");
                 break;
             }
+            case EVENT_RADIO_AVAILABLE: {
+                updateHalCommandToUse();
+                onEvaluate(REQUESTS_UNCHANGED, "EVENT_RADIO_AVAILABLE");
+                break;
+            }
         }
     }
 
@@ -334,8 +352,8 @@
             return;
         }
 
-        // Check if preferred slotId is changed.
-        boolean diffDetected = requestsChanged;
+        // If we use HAL_COMMAND_PREFERRED_DATA,
+        boolean diffDetected = mHalCommandToUse != HAL_COMMAND_PREFERRED_DATA && requestsChanged;
 
         // Check if user setting of default data sub is changed.
         final int dataSub = mSubscriptionController.getDefaultDataSubId();
@@ -366,37 +384,61 @@
 
         if (diffDetected) {
             log("evaluating due to " + sb.toString());
-
-            List<Integer> newActivePhones = new ArrayList<Integer>();
-
-            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() >= mMaxActivePhones) break;
-            }
-
-            if (VDBG) {
-                log("default subId = " + mDefaultDataSubId);
-                log("preferred subId = " + mPreferredDataSubId);
-                for (int i = 0; i < mNumPhones; i++) {
-                    log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
+            if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
+                if (SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) {
+                    mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, null);
                 }
-                log(" newActivePhones:");
-                for (Integer i : newActivePhones) log("  " + i);
-            }
+            } else {
+                List<Integer> newActivePhones = new ArrayList<Integer>();
 
-            for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
-                if (newActivePhones.contains(phoneId) == false) {
-                    deactivate(phoneId);
+                /**
+                 * If all phones can have PS attached, activate all.
+                 * Otherwise, choose to activate phones according to requests. And
+                 * if list is not full, add mPreferredDataPhoneId.
+                 */
+                if (mMaxActivePhones == mPhones.length) {
+                    for (int i = 0; i < mMaxActivePhones; i++) {
+                        newActivePhones.add(mPhones[i].mPhoneId);
+                    }
+                } 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() >= mMaxActivePhones) break;
+                    }
+
+                    if (newActivePhones.size() < mMaxActivePhones
+                            && newActivePhones.contains(mPreferredDataPhoneId)
+                            && SubscriptionManager.isUsableSubIdValue(mPreferredDataPhoneId)) {
+                        newActivePhones.add(mPreferredDataPhoneId);
+                    }
+                }
+
+                if (VDBG) {
+                    log("default subId = " + mDefaultDataSubId);
+                    log("preferred subId = " + mPreferredDataSubId);
+                    for (int i = 0; i < mNumPhones; i++) {
+                        log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
+                    }
+                    log(" newActivePhones:");
+                    for (Integer i : newActivePhones) log("  " + i);
+                }
+
+                for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
+                    if (!newActivePhones.contains(phoneId)) {
+                        deactivate(phoneId);
+                    }
+                }
+
+                // only activate phones up to the limit
+                for (int phoneId : newActivePhones) {
+                    activate(phoneId);
                 }
             }
-
-            // only activate phones up to the limit
-            for (int phoneId : newActivePhones) {
-                activate(phoneId);
-            }
+            // Notify all registrants.
+            mActivePhoneRegistrants.notifyRegistrants();
         }
     }
 
@@ -405,46 +447,48 @@
         public long lastRequested = 0;
     }
 
-    private void deactivate(int phoneId) {
-        PhoneState state = mPhoneStates[phoneId];
-        if (state.active == false) return;
-        state.active = false;
-        log("deactivate " + phoneId);
-        state.lastRequested = System.currentTimeMillis();
-        // Skip ALLOW_DATA for single SIM device
-        if (mNumPhones > 1) {
-            mCommandsInterfaces[phoneId].setDataAllowed(false, null);
-        }
-        mActivePhoneRegistrants[phoneId].notifyRegistrants();
-    }
-
     private void activate(int phoneId) {
-        PhoneState state = mPhoneStates[phoneId];
-        if (state.active == true) return;
-        state.active = true;
-        log("activate " + phoneId);
-        state.lastRequested = System.currentTimeMillis();
-        // Skip ALLOW_DATA for single SIM device
-        if (mNumPhones > 1) {
-            mCommandsInterfaces[phoneId].setDataAllowed(true, null);
-        }
-        mActivePhoneRegistrants[phoneId].notifyRegistrants();
+        switchPhone(phoneId, true);
     }
 
-    // used when the modem may have been rebooted and we want to resend
-    // setDataAllowed
-    public void resendDataAllowed(int phoneId) {
+    private void deactivate(int phoneId) {
+        switchPhone(phoneId, false);
+    }
+
+    private void switchPhone(int phoneId, boolean active) {
+        PhoneState state = mPhoneStates[phoneId];
+        if (state.active == active) return;
+        state.active = active;
+        log((active ? "activate " : "deactivate ") + phoneId);
+        state.lastRequested = System.currentTimeMillis();
+        if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
+            // Skip ALLOW_DATA for single SIM device
+            if (mNumPhones > 1) {
+                mCommandsInterfaces[phoneId].setDataAllowed(active, null);
+            }
+        }
+    }
+
+    /**
+     * Used when the modem may have been rebooted and we
+     * want to resend setDataAllowed or setPreferredData
+     */
+    public void onRadioCapChanged(int phoneId) {
         validatePhoneId(phoneId);
-        Message msg = obtainMessage(EVENT_RESEND_DATA_ALLOWED);
+        Message msg = obtainMessage(EVENT_RADIO_CAPABILITY_CHANGED);
         msg.arg1 = phoneId;
         msg.sendToTarget();
     }
 
-    private void onResendDataAllowed(Message msg) {
+    private void resendRilCommands(Message msg) {
         final int phoneId = msg.arg1;
-        // Skip ALLOW_DATA for single SIM device
-        if (mNumPhones > 1) {
-            mCommandsInterfaces[phoneId].setDataAllowed(mPhoneStates[phoneId].active, null);
+        if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
+            // Skip ALLOW_DATA for single SIM device
+            if (mNumPhones > 1) {
+                mCommandsInterfaces[phoneId].setDataAllowed(mPhoneStates[phoneId].active, null);
+            }
+        } else {
+            mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, null);
         }
     }
 
@@ -517,27 +561,41 @@
     }
 
     /**
-     * Returns whether phone should handle default network requests.
+     * Returns whether phone should handle network requests
+     * that don't specify a subId.
      */
-    public boolean isActiveForDefaultRequests(int phoneId) {
-        return isPhoneActive(phoneId) && phoneId == mPreferredDataPhoneId;
+    public boolean shouldApplyUnspecifiedRequests(int phoneId) {
+        validatePhoneId(phoneId);
+        if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
+            return phoneId == mPreferredDataPhoneId;
+        } else {
+            return mPhoneStates[phoneId].active && phoneId == mPreferredDataPhoneId;
+        }
     }
 
-    public boolean isPhoneActive(int phoneId) {
+    /**
+     * Returns whether phone should handle network requests
+     * that specify a subId.
+     */
+    public boolean shouldApplySpecifiedRequests(int phoneId) {
         validatePhoneId(phoneId);
-        return mPhoneStates[phoneId].active;
+        // If we use SET_PREFERRED_DATA, always apply specified network requests. Otherwise,
+        // only apply network requests if the phone is active (dataAllowed).
+        return mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA || mPhoneStates[phoneId].active;
     }
 
-    public void registerForActivePhoneSwitch(int phoneId, Handler h, int what, Object o) {
-        validatePhoneId(phoneId);
+    /**
+     * If preferred phone changes, or phone activation status changes, registrants
+     * will be notified.
+     */
+    public void registerForActivePhoneSwitch(Handler h, int what, Object o) {
         Registrant r = new Registrant(h, what, o);
-        mActivePhoneRegistrants[phoneId].add(r);
+        mActivePhoneRegistrants.add(r);
         r.notifyRegistrant();
     }
 
-    public void unregisterForActivePhoneSwitch(int phoneId, Handler h) {
-        validatePhoneId(phoneId);
-        mActivePhoneRegistrants[phoneId].remove(h);
+    public void unregisterForActivePhoneSwitch(Handler h) {
+        mActivePhoneRegistrants.remove(h);
     }
 
     private void validatePhoneId(int phoneId) {
@@ -559,6 +617,11 @@
         }
     }
 
+    private void updateHalCommandToUse() {
+        mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported()
+                ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA;
+    }
+
     private void log(String l) {
         Rlog.d(LOG_TAG, l);
         mLocalLog.log(l);
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index ce7be46..7afdb7e 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -147,28 +147,11 @@
         logd("Constructor - Exit");
     }
 
-    public void updateDataConnectionTracker(int sub) {
-        mPhones[sub].updateDataConnectionTracker();
-    }
-
-    public void enableDataConnectivity(int sub) {
-        mPhones[sub].setInternalDataEnabled(true, null);
-    }
-
-    public void disableDataConnectivity(int sub,
-            Message dataCleanedUpMsg) {
-        mPhones[sub].setInternalDataEnabled(false, dataCleanedUpMsg);
-    }
-
-    public void updateCurrentCarrierInProvider(int sub) {
-        mPhones[sub].updateCurrentCarrierInProvider();
-    }
-
-    public void registerForAllDataDisconnected(int subId, Handler h, int what, Object obj) {
+    public void registerForAllDataDisconnected(int subId, Handler h, int what) {
         int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
 
         if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
-            mPhones[phoneId].registerForAllDataDisconnected(h, what, obj);
+            mPhones[phoneId].registerForAllDataDisconnected(h, what);
         }
     }
 
@@ -180,17 +163,6 @@
         }
     }
 
-    public boolean isDataDisconnected(int subId) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) {
-            return mPhones[phoneId].mDcTracker.isDisconnected();
-        } else {
-            // if we can't find a phone for the given subId, it is disconnected.
-            return true;
-        }
-    }
-
     /**
      * Get phone radio type and access technology.
      *
@@ -454,7 +426,7 @@
                 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=SUCCESS");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS;
                 // The modems may have been restarted and forgotten this
-                mPhoneSwitcher.resendDataAllowed(id);
+                mPhoneSwitcher.onRadioCapChanged(id);
                 mPhones[id].radioCapabilityUpdated(rc);
             }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 638fa31..9181e98 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -32,7 +32,7 @@
 import android.hardware.radio.V1_0.CellInfoTdscdma;
 import android.hardware.radio.V1_0.CellInfoType;
 import android.hardware.radio.V1_0.CellInfoWcdma;
-import android.hardware.radio.V1_0.DataProfileInfo;
+import android.hardware.radio.V1_0.DataProfileId;
 import android.hardware.radio.V1_0.Dial;
 import android.hardware.radio.V1_0.GsmBroadcastSmsConfigInfo;
 import android.hardware.radio.V1_0.GsmSmsMessage;
@@ -100,6 +100,8 @@
 import com.android.internal.telephony.cat.ComprehensionTlvTag;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.dataconnection.TransportManager.IwlanOperationMode;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
@@ -153,6 +155,21 @@
     public static final int FOR_ACK_WAKELOCK = 1;
     private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
 
+    private static final HalVersion RADIO_HAL_VERSION_UNKNOWN = new HalVersion(-1, -1);
+
+    private static final HalVersion RADIO_HAL_VERSION_1_0 = new HalVersion(1, 0);
+
+    private static final HalVersion RADIO_HAL_VERSION_1_1 = new HalVersion(1, 1);
+
+    private static final HalVersion RADIO_HAL_VERSION_1_2 = new HalVersion(1, 2);
+
+    private static final HalVersion RADIO_HAL_VERSION_1_3 = new HalVersion(1, 3);
+
+    private static final HalVersion RADIO_HAL_VERSION_1_4 = new HalVersion(1, 4);
+
+    // IRadio version
+    private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
+
     //***** Instance Variables
 
     final WakeLock mWakeLock;           // Wake lock associated with request/response
@@ -353,7 +370,7 @@
         // increment the cookie so that death notification can be ignored
         mRadioProxyCookie.incrementAndGet();
 
-        setRadioState(RadioState.RADIO_UNAVAILABLE, true /* forceNotifyRegistrants */);
+        setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE, true /* forceNotifyRegistrants */);
 
         RILRequest.resetSerial();
         // Clear request list on close
@@ -385,8 +402,49 @@
                 riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                         + " is disabled");
             } else {
-                mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId],
-                        true);
+                try {
+                    mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
+                            HIDL_SERVICE_NAME[mPhoneId], true);
+                    mRadioVersion = RADIO_HAL_VERSION_1_4;
+                } catch (NoSuchElementException e) {
+                }
+
+                if (mRadioProxy == null) {
+                    try {
+                        mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
+                                HIDL_SERVICE_NAME[mPhoneId], true);
+                        mRadioVersion = RADIO_HAL_VERSION_1_3;
+                    } catch (NoSuchElementException e) {
+                    }
+                }
+
+                if (mRadioProxy == null) {
+                    try {
+                        mRadioProxy = android.hardware.radio.V1_2.IRadio.getService(
+                                HIDL_SERVICE_NAME[mPhoneId], true);
+                        mRadioVersion = RADIO_HAL_VERSION_1_2;
+                    } catch (NoSuchElementException e) {
+                    }
+                }
+
+                if (mRadioProxy == null) {
+                    try {
+                        mRadioProxy = android.hardware.radio.V1_1.IRadio.getService(
+                                HIDL_SERVICE_NAME[mPhoneId], true);
+                        mRadioVersion = RADIO_HAL_VERSION_1_1;
+                    } catch (NoSuchElementException e) {
+                    }
+                }
+
+                if (mRadioProxy == null) {
+                    try {
+                        mRadioProxy = android.hardware.radio.V1_0.IRadio.getService(
+                                HIDL_SERVICE_NAME[mPhoneId], true);
+                        mRadioVersion = RADIO_HAL_VERSION_1_0;
+                    } catch (NoSuchElementException e) {
+                    }
+                }
+
                 if (mRadioProxy != null) {
                     mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
                             mRadioProxyCookie.incrementAndGet());
@@ -397,9 +455,6 @@
                             + HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
                 }
             }
-        } catch (NoSuchElementException e) {
-            mRadioProxy = null;
-            riljLoge("IRadio service is not on the device HAL: " + e);
         } catch (RemoteException e) {
             mRadioProxy = null;
             riljLoge("RadioProxy getService/setResponseFunctions: " + e);
@@ -522,6 +577,10 @@
         // wakelock stuff is initialized above as callbacks are received on separate binder threads)
         getRadioProxy(null);
         getOemHookProxy(null);
+
+        if (RILJ_LOGD) {
+            riljLog("Radio HAL version: " + mRadioVersion);
+        }
     }
 
     @Override
@@ -1156,12 +1215,14 @@
     }
 
     /**
-     * Convert to DataProfileInfo defined in types.hal
+     * Convert to DataProfileInfo defined in radio/1.0/types.hal
      * @param dp Data profile
      * @return A converted data profile
      */
-    private static DataProfileInfo convertToHalDataProfile(DataProfile dp) {
-        DataProfileInfo dpi = new DataProfileInfo();
+    private static android.hardware.radio.V1_0.DataProfileInfo convertToHalDataProfile10(
+            DataProfile dp) {
+        android.hardware.radio.V1_0.DataProfileInfo dpi =
+                new android.hardware.radio.V1_0.DataProfileInfo();
 
         dpi.profileId = dp.getProfileId();
         dpi.apn = dp.getApn();
@@ -1178,8 +1239,41 @@
         dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmap();
         dpi.bearerBitmap = dp.getBearerBitmap();
         dpi.mtu = dp.getMtu();
-        dpi.mvnoType = convertToHalMvnoType(dp.getMvnoType());
-        dpi.mvnoMatchData = dp.getMvnoMatchData();
+        dpi.mvnoType = MvnoType.NONE;
+        dpi.mvnoMatchData = "";
+
+        return dpi;
+    }
+
+    /**
+     * Convert to DataProfileInfo defined in radio/1.4/types.hal
+     * @param dp Data profile
+     * @return A converted data profile
+     */
+    private static android.hardware.radio.V1_4.DataProfileInfo convertToHalDataProfile14(
+            DataProfile dp) {
+        android.hardware.radio.V1_4.DataProfileInfo dpi =
+                new android.hardware.radio.V1_4.DataProfileInfo();
+
+        dpi.apn = dp.getApn();
+        dpi.protocol = dp.getProtocol();
+        dpi.roamingProtocol = dp.getRoamingProtocol();
+        dpi.authType = dp.getAuthType();
+        dpi.user = dp.getUserName();
+        dpi.password = dp.getPassword();
+        dpi.type = dp.getType();
+        dpi.maxConnsTime = dp.getMaxConnsTime();
+        dpi.maxConns = dp.getMaxConns();
+        dpi.waitTime = dp.getWaitTime();
+        dpi.enabled = dp.isEnabled();
+        dpi.supportedApnTypesBitmap = dp.getSupportedApnTypesBitmap();
+        dpi.bearerBitmap = dp.getBearerBitmap();
+        dpi.mtu = dp.getMtu();
+        dpi.persistent = dp.isPersistent();
+        dpi.preferred = dp.isPreferred();
+
+        // profile id is only meaningful when it's persistent on the modem.
+        dpi.profileId = (dpi.persistent) ? dp.getProfileId() : DataProfileId.INVALID;
 
         return dpi;
     }
@@ -1216,19 +1310,67 @@
             RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
                     mRILDefaultWorkSource);
 
-            // Convert to HAL data profile
-            DataProfileInfo dpi = convertToHalDataProfile(dataProfile);
+            ArrayList<String> addresses = new ArrayList<>();
+            ArrayList<String> dnses = new ArrayList<>();
+            if (linkProperties != null) {
+                for (InetAddress address : linkProperties.getAddresses()) {
+                    addresses.add(address.getHostAddress());
+                }
+                for (InetAddress dns : linkProperties.getDnsServers()) {
+                    dnses.add(dns.getHostAddress());
+                }
+            }
 
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
             try {
-                if (radioProxy12 == null) {
-                    // IRadio V1.0
+                if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+                    // IRadio V1.4
+                    android.hardware.radio.V1_4.IRadio radioProxy14 =
+                            (android.hardware.radio.V1_4.IRadio) radioProxy;
 
-                    // Getting data RAT here is just a workaround to support the older 1.0 vendor
-                    // RIL. The new data service interface passes access network type instead of
-                    // RAT for setup data request. It is impossible to convert access network
-                    // type back to RAT here, so we directly get the data RAT from phone.
+                    // Convert to HAL data profile
+                    android.hardware.radio.V1_4.DataProfileInfo dpi =
+                            convertToHalDataProfile14(dataProfile);
+
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                + ",accessNetworkType=" + accessNetworkType + ",isRoaming="
+                                + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
+                                + ",addresses=" + addresses + ",dnses=" + dnses);
+                    }
+
+                    radioProxy14.setupDataCall_1_4(rr.mSerial, accessNetworkType, dpi, allowRoaming,
+                            reason, addresses, dnses);
+                } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+                    // IRadio V1.2 and IRadio V1.3
+                    android.hardware.radio.V1_2.IRadio radioProxy12 =
+                            (android.hardware.radio.V1_2.IRadio) radioProxy;
+
+                    // Convert to HAL data profile
+                    android.hardware.radio.V1_0.DataProfileInfo dpi =
+                            convertToHalDataProfile10(dataProfile);
+
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                + ",accessNetworkType=" + accessNetworkType + ",isRoaming="
+                                + isRoaming + ",allowRoaming=" + allowRoaming + ","
+                                + dataProfile + ",addresses=" + addresses + ",dnses=" + dnses);
+                    }
+
+                    radioProxy12.setupDataCall_1_2(rr.mSerial, accessNetworkType, dpi,
+                            dataProfile.isPersistent(), allowRoaming, isRoaming, reason,
+                            addresses, dnses);
+                } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_0)) {
+                    // IRadio V1.0 and IRadio V1.1
+
+                    // Convert to HAL data profile
+                    android.hardware.radio.V1_0.DataProfileInfo dpi =
+                            convertToHalDataProfile10(dataProfile);
+
+                    // Getting data RAT here is just a workaround to support the older 1.0
+                    // vendor RIL. The new data service interface passes access network type
+                    // instead of RAT for setup data request. It is impossible to convert access
+                    // network type back to RAT here, so we directly get the data RAT from
+                    // phone.
                     int dataRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
                     Phone phone = PhoneFactory.getPhone(mPhoneId);
                     if (phone != null) {
@@ -1244,30 +1386,7 @@
                     }
 
                     radioProxy.setupDataCall(rr.mSerial, dataRat, dpi,
-                            dataProfile.isModemCognitive(), allowRoaming, isRoaming);
-                } else {
-                    // IRadio V1.2
-                    ArrayList<String> addresses = new ArrayList<>();
-                    ArrayList<String> dnses = new ArrayList<>();
-                    if (linkProperties != null) {
-                        for (InetAddress address : linkProperties.getAddresses()) {
-                            addresses.add(address.getHostAddress());
-                        }
-                        for (InetAddress dns : linkProperties.getDnsServers()) {
-                            dnses.add(dns.getHostAddress());
-                        }
-                    }
-
-                    if (RILJ_LOGD) {
-                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                                + ",accessNetworkType=" + accessNetworkType + ",isRoaming="
-                                + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
-                                + ",addresses=" + addresses + ",dnses=" + dnses);
-                    }
-
-                    radioProxy12.setupDataCall_1_2(rr.mSerial, accessNetworkType, dpi,
-                            dataProfile.isModemCognitive(), allowRoaming, isRoaming, reason,
-                            addresses, dnses);
+                            dataProfile.isPersistent(), allowRoaming, isRoaming);
                 }
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "setupDataCall", e);
@@ -1552,15 +1671,15 @@
                         + requestToString(rr.mRequest) + " cid = " + cid + " reason = " + reason);
             }
 
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-
             try {
-                if (radioProxy12 == null) {
+                if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+                    android.hardware.radio.V1_2.IRadio radioProxy12 =
+                            (android.hardware.radio.V1_2.IRadio) radioProxy;
+
+                    radioProxy12.deactivateDataCall_1_2(rr.mSerial, cid, reason);
+                } else {
                     radioProxy.deactivateDataCall(rr.mSerial, cid,
                             (reason == DataService.REQUEST_REASON_SHUTDOWN));
-                } else {
-                    radioProxy12.deactivateDataCall_1_2(rr.mSerial, cid, reason);
                 }
                 mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
             } catch (RemoteException | RuntimeException e) {
@@ -1771,9 +1890,10 @@
     public void startNetworkScan(NetworkScanRequest nsr, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-            if (radioProxy12 != null) {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+                android.hardware.radio.V1_2.IRadio radioProxy12 =
+                        (android.hardware.radio.V1_2.IRadio) radioProxy;
+
                 android.hardware.radio.V1_2.NetworkScanRequest request =
                         new android.hardware.radio.V1_2.NetworkScanRequest();
                 request.type = nsr.getScanType();
@@ -1806,43 +1926,40 @@
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
                 }
-            } else {
+            } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
                 android.hardware.radio.V1_1.IRadio radioProxy11 =
-                        android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-                if (radioProxy11 == null) {
-                    if (result != null) {
-                        AsyncResult.forMessage(result, null,
-                                CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                        result.sendToTarget();
-                    }
-                } else {
-                    android.hardware.radio.V1_1.NetworkScanRequest request =
-                            new android.hardware.radio.V1_1.NetworkScanRequest();
-                    request.type = nsr.getScanType();
-                    request.interval = nsr.getSearchPeriodicity();
-                    for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
-                        android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
-                                convertRadioAccessSpecifierToRadioHAL(ras);
-                        if (rasInHalFormat == null) {
-                            return;
-                        }
+                        (android.hardware.radio.V1_1.IRadio) radioProxy;
 
-                        request.specifiers.add(rasInHalFormat);
+                android.hardware.radio.V1_1.NetworkScanRequest request =
+                        new android.hardware.radio.V1_1.NetworkScanRequest();
+                request.type = nsr.getScanType();
+                request.interval = nsr.getSearchPeriodicity();
+                for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
+                    android.hardware.radio.V1_1.RadioAccessSpecifier rasInHalFormat =
+                            convertRadioAccessSpecifierToRadioHAL(ras);
+                    if (rasInHalFormat == null) {
+                        return;
                     }
 
-                    RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
-                            mRILDefaultWorkSource);
-
-                    if (RILJ_LOGD) {
-                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-                    }
-
-                    try {
-                        radioProxy11.startNetworkScan(rr.mSerial, request);
-                    } catch (RemoteException | RuntimeException e) {
-                        handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
-                    }
+                    request.specifiers.add(rasInHalFormat);
                 }
+
+                RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
+                        mRILDefaultWorkSource);
+
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+
+                try {
+                    radioProxy11.startNetworkScan(rr.mSerial, request);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
+                }
+            } else if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
             }
         }
     }
@@ -1851,15 +1968,10 @@
     public void stopNetworkScan(Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_1.IRadio radioProxy11 =
-                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-            if (radioProxy11 == null) {
-                if (result != null) {
-                    AsyncResult.forMessage(result, null,
-                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                    result.sendToTarget();
-                }
-            } else {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+                android.hardware.radio.V1_1.IRadio radioProxy11 =
+                        (android.hardware.radio.V1_1.IRadio) radioProxy;
+
                 RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
                         mRILDefaultWorkSource);
 
@@ -1872,6 +1984,10 @@
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "stopNetworkScan", e);
                 }
+            } else if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
             }
         }
     }
@@ -2239,7 +2355,7 @@
                         byte[] target = Arrays.copyOfRange(ctlv.getRawValue(), from,
                                 ctlv.getValueIndex() + ctlv.getLength());
                         terminalResponse = terminalResponse.toLowerCase().replace(
-                                IccUtils.bytesToHexString(target), "********");
+                                IccUtils.bytesToHexString(target).toLowerCase(), "********");
                     }
                     // The text string tag and the length field should also be hidden.
                     from = ctlv.getValueIndex() + ctlv.getLength();
@@ -3039,8 +3155,18 @@
             }
 
             try {
-                radioProxy.setInitialAttachApn(rr.mSerial, convertToHalDataProfile(dataProfile),
-                        dataProfile.isModemCognitive(), isRoaming);
+                if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+                    // v1.4
+                    android.hardware.radio.V1_4.IRadio radioProxy14 =
+                            (android.hardware.radio.V1_4.IRadio) radioProxy;
+                    radioProxy14.setInitialAttachApn_1_4(rr.mSerial,
+                            convertToHalDataProfile14(dataProfile));
+                } else {
+                    // v1.3, v1.2, v1.1, and v1.0
+                    radioProxy.setInitialAttachApn(rr.mSerial,
+                            convertToHalDataProfile10(dataProfile), dataProfile.isPersistent(),
+                            isRoaming);
+                }
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "setInitialAttachApn", e);
             }
@@ -3426,24 +3552,57 @@
 
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
-                    mRILDefaultWorkSource);
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                        + " with data profiles : ");
-                for (DataProfile profile : dps) {
-                    riljLog(profile.toString());
-                }
-            }
-
-            ArrayList<DataProfileInfo> dpis = new ArrayList<>();
-            for (DataProfile dp : dps) {
-                dpis.add(convertToHalDataProfile(dp));
-            }
-
+            RILRequest rr = null;
             try {
-                radioProxy.setDataProfile(rr.mSerial, dpis, isRoaming);
+                if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+                    // V1.4
+                    android.hardware.radio.V1_4.IRadio radioProxy14 =
+                            (android.hardware.radio.V1_4.IRadio) radioProxy;
+
+                    rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
+                            mRILDefaultWorkSource);
+
+                    ArrayList<android.hardware.radio.V1_4.DataProfileInfo> dpis = new ArrayList<>();
+                    for (DataProfile dp : dps) {
+                        dpis.add(convertToHalDataProfile14(dp));
+                    }
+
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                + " with data profiles : ");
+                        for (DataProfile profile : dps) {
+                            riljLog(profile.toString());
+                        }
+                    }
+
+                    radioProxy14.setDataProfile_1_4(rr.mSerial, dpis);
+                } else {
+                    // V1.0, 1.1, 1,2 and 1.3
+                    ArrayList<android.hardware.radio.V1_0.DataProfileInfo> dpis = new ArrayList<>();
+                    for (DataProfile dp : dps) {
+                        // For v1.0 to v1.2, we only send data profiles that has the persistent
+                        // (a.k.a modem cognitive) bit set to true.
+                        if (dp.isPersistent()) {
+                            dpis.add(convertToHalDataProfile10(dp));
+                        }
+                    }
+
+                    if (!dpis.isEmpty()) {
+                        rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
+                                mRILDefaultWorkSource);
+
+                        if (RILJ_LOGD) {
+                            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                    + " with data profiles : ");
+                            for (DataProfile profile : dps) {
+                                riljLog(profile.toString());
+                            }
+                        }
+
+                        radioProxy.setDataProfile(rr.mSerial, dpis, isRoaming);
+                    }
+                }
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "setDataProfile", e);
             }
@@ -3520,9 +3679,8 @@
     @Override
     public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
         IRadio radioProxy = getRadioProxy(result);
-        android.hardware.radio.V1_2.IRadio radioProxy12 =
-                android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-        if (radioProxy12 != null) {
+
+        if (mRadioVersion.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.
             return;
@@ -3548,9 +3706,7 @@
     @Override
     public void stopLceService(Message result) {
         IRadio radioProxy = getRadioProxy(result);
-        android.hardware.radio.V1_2.IRadio radioProxy12 =
-                android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-        if (radioProxy12 != null) {
+        if (mRadioVersion.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.
             return;
@@ -3744,11 +3900,11 @@
                 riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + filter);
             }
 
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-
-            if (radioProxy12 != null) {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
                 try {
+                    android.hardware.radio.V1_2.IRadio radioProxy12 =
+                            (android.hardware.radio.V1_2.IRadio) radioProxy;
+
                     radioProxy12.setIndicationFilter_1_2(rr.mSerial, filter);
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "setIndicationFilter_1_2", e);
@@ -3769,10 +3925,9 @@
             int[] thresholdsDbm, int ran, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-            if (radioProxy12 == null) {
-                riljLoge("setSignalStrengthReportingCriteria ignored. RadioProxy 1.2 is null!");
+            if (mRadioVersion.less(RADIO_HAL_VERSION_1_2)) {
+                riljLoge("setSignalStrengthReportingCriteria ignored on IRadio version less "
+                        + "than 1.2");
                 return;
             }
 
@@ -3784,6 +3939,8 @@
             }
 
             try {
+                android.hardware.radio.V1_2.IRadio radioProxy12 =
+                        (android.hardware.radio.V1_2.IRadio) radioProxy;
                 radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial, hysteresisMs,
                         hysteresisDb, primitiveArrayToArrayList(thresholdsDbm),
                         convertRanToHalRan(ran));
@@ -3799,10 +3956,9 @@
             Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_2.IRadio radioProxy12 =
-                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
-            if (radioProxy12 == null) {
-                riljLoge("setLinkCapacityReportingCriteria ignored. RadioProxy 1.2 is null!");
+            if (mRadioVersion.less(RADIO_HAL_VERSION_1_2)) {
+                riljLoge("setLinkCapacityReportingCriteria ignored on IRadio version less "
+                        + "than 1.2");
                 return;
             }
 
@@ -3814,6 +3970,8 @@
             }
 
             try {
+                android.hardware.radio.V1_2.IRadio radioProxy12 =
+                        (android.hardware.radio.V1_2.IRadio) radioProxy;
                 radioProxy12.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
                         hysteresisDlKbps, hysteresisUlKbps,
                         primitiveArrayToArrayList(thresholdsDlKbps),
@@ -3853,9 +4011,16 @@
             if (RILJ_LOGD) {
                 riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + state);
             }
-            android.hardware.radio.V1_1.IRadio radioProxy11 =
-                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-            if (radioProxy11 == null) {
+
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+                try {
+                    android.hardware.radio.V1_1.IRadio radioProxy11 =
+                            (android.hardware.radio.V1_1.IRadio) radioProxy;
+                    radioProxy11.setSimCardPower_1_1(rr.mSerial, state);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
+                }
+            } else {
                 try {
                     switch (state) {
                         case TelephonyManager.CARD_POWER_DOWN: {
@@ -3877,12 +4042,6 @@
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
                 }
-            } else {
-                try {
-                    radioProxy11.setSimCardPower_1_1(rr.mSerial, state);
-                } catch (RemoteException | RuntimeException e) {
-                    handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
-                }
             }
         }
     }
@@ -3893,15 +4052,10 @@
         checkNotNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            android.hardware.radio.V1_1.IRadio radioProxy11 =
-                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-            if (radioProxy11 == null) {
-                if (result != null) {
-                    AsyncResult.forMessage(result, null,
-                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
-                    result.sendToTarget();
-                }
-            } else {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+                android.hardware.radio.V1_1.IRadio radioProxy11 =
+                        (android.hardware.radio.V1_1.IRadio ) radioProxy;
+
                 RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
                         mRILDefaultWorkSource);
                 if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
@@ -3925,6 +4079,10 @@
                 } catch (RemoteException | RuntimeException e) {
                     handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
                 }
+            } else if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
             }
         }
     }
@@ -3939,9 +4097,7 @@
             return;
         }
 
-        android.hardware.radio.V1_1.IRadio radioProxy11 =
-                android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-        if (radioProxy11 == null) {
+        if (mRadioVersion.less(RADIO_HAL_VERSION_1_1)) {
             if (result != null) {
                 AsyncResult.forMessage(result, null,
                         CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
@@ -3950,6 +4106,9 @@
             return;
         }
 
+        android.hardware.radio.V1_1.IRadio radioProxy11 =
+                (android.hardware.radio.V1_1.IRadio) radioProxy;
+
         RILRequest rr = obtainRequest(
                 RIL_REQUEST_START_KEEPALIVE, result, mRILDefaultWorkSource);
 
@@ -3994,9 +4153,7 @@
             return;
         }
 
-        android.hardware.radio.V1_1.IRadio radioProxy11 =
-                android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
-        if (radioProxy11 == null) {
+        if (mRadioVersion.less(RADIO_HAL_VERSION_1_1)) {
             if (result != null) {
                 AsyncResult.forMessage(result, null,
                         CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
@@ -4005,6 +4162,9 @@
             return;
         }
 
+        android.hardware.radio.V1_1.IRadio radioProxy11 =
+                (android.hardware.radio.V1_1.IRadio) radioProxy;
+
         RILRequest rr = obtainRequest(
                 RIL_REQUEST_STOP_KEEPALIVE, result, mRILDefaultWorkSource);
 
@@ -4188,7 +4348,8 @@
                 }
                 break;
             case RIL_REQUEST_SHUTDOWN:
-                setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
+                setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE,
+                        false /* forceNotifyRegistrants */);
                 break;
         }
 
@@ -5206,7 +5367,8 @@
             android.hardware.radio.V1_0.RadioCapability rcRil, RIL ril) {
         int session = rcRil.session;
         int phase = rcRil.phase;
-        int rat = rcRil.raf;
+        // convert to public bitmask {@link TelephonyManager.NetworkTypeBitMask}
+        int rat = RadioAccessFamily.convertToNetworkTypeBitMask(rcRil.raf);
         String logicModemUuid = rcRil.logicalModemUuid;
         int status = rcRil.status;
 
@@ -5214,7 +5376,7 @@
                 ", phase=" + phase +
                 ", rat=" + rat +
                 ", logicModemUuid=" + logicModemUuid +
-                ", status=" + status);
+                ", status=" + status + ", rcRil.raf=" + rcRil.raf);
         RadioCapability rc = new RadioCapability(
                 ril.mPhoneId, session, phase, rat, logicModemUuid, status);
         return rc;
@@ -5240,6 +5402,7 @@
         return lce;
     }
 
+    // TODO(b/119224773) refactor the converter of CellInfo.
     private static void writeToParcelForGsm(
             Parcel p, int lac, int cid, int arfcn, int bsic, String mcc, String mnc,
             String al, String as, int ss, int ber, int ta) {
@@ -5257,6 +5420,7 @@
         p.writeInt(ta);
     }
 
+    // TODO(b/119224773) refactor the converter of CellInfo.
     private static void writeToParcelForCdma(
             Parcel p, int ni, int si, int bsi, int lon, int lat, String al, String as,
             int dbm, int ecio, int eDbm, int eEcio, int eSnr) {
@@ -5264,27 +5428,39 @@
         new CellSignalStrengthCdma(dbm, ecio, eDbm, eEcio, eSnr).writeToParcel(p, 0);
     }
 
+    // TODO(b/119224773) refactor the converter of CellInfo.
     private static void writeToParcelForLte(
             Parcel p, int ci, int pci, int tac, int earfcn, int bandwidth, String mcc, String mnc,
-            String al, String as, int ss, int rsrp, int rsrq, int rssnr, int cqi, int ta) {
+            String al, String as, int ss, int rsrp, int rsrq, int rssnr, int cqi, int ta,
+            boolean isEndcAvailable) {
+
+        // General CellInfo
         p.writeInt(CellInfo.TYPE_LTE);
         p.writeString(mcc);
         p.writeString(mnc);
         p.writeString(al);
         p.writeString(as);
+
+        // CellIdentity
         p.writeInt(ci);
         p.writeInt(pci);
         p.writeInt(tac);
         p.writeInt(earfcn);
         p.writeInt(bandwidth);
+
+        // CellSignalStrength
         p.writeInt(ss);
         p.writeInt(rsrp);
         p.writeInt(rsrq);
         p.writeInt(rssnr);
         p.writeInt(cqi);
         p.writeInt(ta);
+
+        // CellConfigLte
+        p.writeBoolean(isEndcAvailable);
     }
 
+    // TODO(b/119224773) refactor the converter of CellInfo.
     private static void writeToParcelForWcdma(
             Parcel p, int lac, int cid, int psc, int uarfcn, String mcc, String mnc,
             String al, String as, int ss, int ber, int rscp, int ecno) {
@@ -5303,6 +5479,7 @@
         p.writeInt(ecno);
     }
 
+    // TODO(b/119224773) refactor the converter of CellInfo.
     private static void writeToParcelForTdscdma(
             Parcel p, int lac, int cid, int cpid, int uarfcn, String mcc, String mnc,
             String al, String as, int ss, int ber, int rscp) {
@@ -5335,8 +5512,7 @@
             Parcel p = Parcel.obtain();
             p.writeInt(record.cellInfoType);
             p.writeInt(record.registered ? 1 : 0);
-            p.writeInt(record.timeStampType);
-            p.writeLong(record.timeStamp);
+            p.writeLong(SystemClock.elapsedRealtimeNanos());
             p.writeInt(CellInfo.CONNECTION_UNKNOWN);
             switch (record.cellInfoType) {
                 case CellInfoType.GSM: {
@@ -5394,7 +5570,8 @@
                             cellInfoLte.signalStrengthLte.rsrq,
                             cellInfoLte.signalStrengthLte.rssnr,
                             cellInfoLte.signalStrengthLte.cqi,
-                            cellInfoLte.signalStrengthLte.timingAdvance);
+                            cellInfoLte.signalStrengthLte.timingAdvance,
+                            false /* isEndcAvailable */);
                     break;
                 }
 
@@ -5462,8 +5639,7 @@
             Parcel p = Parcel.obtain();
             p.writeInt(record.cellInfoType);
             p.writeInt(record.registered ? 1 : 0);
-            p.writeInt(record.timeStampType);
-            p.writeLong(record.timeStamp);
+            p.writeLong(SystemClock.elapsedRealtimeNanos());
             p.writeInt(record.connectionStatus);
             switch (record.cellInfoType) {
                 case CellInfoType.GSM: {
@@ -5521,7 +5697,8 @@
                             cellInfoLte.signalStrengthLte.rsrq,
                             cellInfoLte.signalStrengthLte.rssnr,
                             cellInfoLte.signalStrengthLte.cqi,
-                            cellInfoLte.signalStrengthLte.timingAdvance);
+                            cellInfoLte.signalStrengthLte.timingAdvance,
+                            false /* isEndcAvailable */);
                     break;
                 }
 
@@ -5629,4 +5806,27 @@
                 signalStrength.wcdma.base.signalStrength,
                 signalStrength.wcdma.rscp);
     }
+
+    /**
+     * @return The {@link IwlanOperationMode IWLAN operation mode}
+     */
+    public @IwlanOperationMode int getIwlanOperationMode() {
+        // Get IWLAN operation mode from the system property. If the system property is missing,
+        // use the default mode.
+        int mode = SystemProperties.getInt(TransportManager.SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE,
+                TransportManager.IWLAN_OPERATION_MODE_DEFAULT);
+
+        // If the operation mode is default, then we use the HAL version to determine it.
+        // On 1.4 or later version of IRadio, it is expected the device to support
+        // IWLAN AP-assisted mode.
+        if (mode == TransportManager.IWLAN_OPERATION_MODE_DEFAULT) {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+                return TransportManager.IWLAN_OPERATION_MODE_AP_ASSISTED;
+            } else {
+                return TransportManager.IWLAN_OPERATION_MODE_LEGACY;
+            }
+        }
+
+        return mode;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/RadioCapability.java b/src/java/com/android/internal/telephony/RadioCapability.java
index a7e73fc..d405c92 100644
--- a/src/java/com/android/internal/telephony/RadioCapability.java
+++ b/src/java/com/android/internal/telephony/RadioCapability.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.telephony.TelephonyManager;
+
 /**
  * Object to indicate the phone radio capability.
  *
@@ -94,8 +96,9 @@
      * RadioAccessFamily is a bit field of radio access technologies the
      * for the modem is currently supporting. The initial value returned
      * my the modem must the the set of bits that the modem currently supports.
-     * see RadioAccessFamily#RADIO_TECHNOLOGY_XXXX
+     * see {@link android.telephony.TelephonyManager.NetworkTypeBitMask}
      */
+    @TelephonyManager.NetworkTypeBitMask
     private int mRadioAccessFamily;
 
     /**
@@ -118,14 +121,15 @@
      * @param session the request transaction id
      * @param phase the request phase id
      * @param radioAccessFamily the phone radio access family defined in
-     *        RadioAccessFamily. It's a bit mask value to represent
-     *        the support type.
+     * {@link android.telephony.TelephonyManager.NetworkTypeBitMask}
+     *                          It's a bit mask value to represent the support type.
      * @param logicalModemUuid the logicalModem UUID which phone connected to
      * @param status tell modem the action transaction of
      *        set radio capability is success or fail with RC_Phase_FINISH
      */
     public RadioCapability(int phoneId, int session, int phase,
-            int radioAccessFamily, String logicalModemUuid, int status) {
+                           @TelephonyManager.NetworkTypeBitMask int radioAccessFamily,
+                           String logicalModemUuid, int status) {
         mPhoneId = phoneId;
         mSession = session;
         mPhase = phase;
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index c258f6c..3706dc1 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -17,14 +17,15 @@
 package com.android.internal.telephony;
 
 import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE;
+import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
+import static com.android.internal.telephony.RILConstants
+        .RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
 
 import android.content.Context;
 import android.hardware.radio.V1_0.RadioResponseInfo;
 import android.hardware.radio.V1_0.RadioResponseType;
 import android.hardware.radio.config.V1_0.IRadioConfig;
-import android.hardware.radio.config.V1_0.SimSlotStatus;
 import android.net.ConnectivityManager;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -258,7 +259,32 @@
     }
 
     /**
-     * Wrapper function for IRadioConfig.getSimSlotsStatus().
+     * Wrapper function for IRadioConfig.setPreferredDataModem(int modemId).
+     */
+    public void setPreferredDataModem(int modemId, Message result) {
+        if (!isSetPreferredDataCommandSupported()) {
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                result.sendToTarget();
+            }
+        }
+        // TODO: call radioConfigProxy.setPreferredDataModem when it's ready.
+    }
+
+    /**
+     * @return whether current radio config version supports SET_PREFERRED_DATA_MODEM command.
+     * If yes, we'll use RIL_REQUEST_SET_PREFERRED_DATA_MODEM to indicate which modem is preferred.
+     * If not, we shall use RIL_REQUEST_ALLOW_DATA for on-demand PS attach / detach.
+     * See PhoneSwitcher for more details.
+     */
+    public boolean isSetPreferredDataCommandSupported() {
+        // TODO: call radioConfigProxy.isSetPreferredDataCommandSupported when it's ready.
+        return false;
+    }
+
+    /**
+     * Wrapper function for IRadioConfig.setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap).
      */
     public void setSimSlotsMapping(int[] physicalSlots, Message result) {
         IRadioConfig radioConfigProxy = getRadioConfigProxy(result);
@@ -317,9 +343,9 @@
     }
 
     static ArrayList<IccSlotStatus> convertHalSlotStatus(
-            ArrayList<SimSlotStatus> halSlotStatusList) {
+            ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> halSlotStatusList) {
         ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size());
-        for (SimSlotStatus slotStatus : halSlotStatusList) {
+        for (android.hardware.radio.config.V1_0.SimSlotStatus slotStatus : halSlotStatusList) {
             IccSlotStatus iccSlotStatus = new IccSlotStatus();
             iccSlotStatus.setCardState(slotStatus.cardState);
             iccSlotStatus.setSlotState(slotStatus.slotState);
@@ -331,6 +357,22 @@
         return response;
     }
 
+    static ArrayList<IccSlotStatus> convertHalSlotStatus_1_2(
+            ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> halSlotStatusList) {
+        ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size());
+        for (android.hardware.radio.config.V1_2.SimSlotStatus slotStatus : halSlotStatusList) {
+            IccSlotStatus iccSlotStatus = new IccSlotStatus();
+            iccSlotStatus.setCardState(slotStatus.base.cardState);
+            iccSlotStatus.setSlotState(slotStatus.base.slotState);
+            iccSlotStatus.logicalSlotIndex = slotStatus.base.logicalSlotId;
+            iccSlotStatus.atr = slotStatus.base.atr;
+            iccSlotStatus.iccid = slotStatus.base.iccid;
+            iccSlotStatus.eid = slotStatus.eid;
+            response.add(iccSlotStatus);
+        }
+        return response;
+    }
+
     private static void logd(String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndication.java b/src/java/com/android/internal/telephony/RadioConfigIndication.java
index 686282c..5774bb1 100644
--- a/src/java/com/android/internal/telephony/RadioConfigIndication.java
+++ b/src/java/com/android/internal/telephony/RadioConfigIndication.java
@@ -17,7 +17,6 @@
 package com.android.internal.telephony;
 
 import android.hardware.radio.config.V1_0.IRadioConfigIndication;
-import android.hardware.radio.config.V1_0.SimSlotStatus;
 import android.os.AsyncResult;
 import android.telephony.Rlog;
 
@@ -39,7 +38,8 @@
     /**
      * Unsolicited indication for slot status changed
      */
-    public void simSlotsStatusChanged(int indicationType, ArrayList<SimSlotStatus> slotStatus) {
+    public void simSlotsStatusChanged(int indicationType,
+            ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> slotStatus) {
         ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus(slotStatus);
         Rlog.d(TAG, "[UNSL]< " + " UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
         if (mRadioConfig.mSimSlotStatusRegistrant != null) {
@@ -47,4 +47,17 @@
                     new AsyncResult(null, ret, null));
         }
     }
+
+    /**
+     * Unsolicited indication for slot status changed
+     */
+    public void simSlotsStatusChanged_1_2(int indicationType,
+            ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> slotStatus) {
+        ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus_1_2(slotStatus);
+        Rlog.d(TAG, "[UNSL]< " + " UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
+        if (mRadioConfig.mSimSlotStatusRegistrant != null) {
+            mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
+                    new AsyncResult(null, ret, null));
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index 8177b2d..3b333ae 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -18,8 +18,8 @@
 
 import android.hardware.radio.V1_0.RadioError;
 import android.hardware.radio.V1_0.RadioResponseInfo;
-import android.hardware.radio.config.V1_0.IRadioConfigResponse;
-import android.hardware.radio.config.V1_0.SimSlotStatus;
+import android.hardware.radio.config.V1_1.PhoneCapability;
+import android.hardware.radio.config.V1_2.IRadioConfigResponse;
 import android.telephony.Rlog;
 
 import com.android.internal.telephony.uicc.IccSlotStatus;
@@ -41,7 +41,7 @@
      * Response function for IRadioConfig.getSimSlotsStatus().
      */
     public void getSimSlotsStatusResponse(RadioResponseInfo responseInfo,
-                                          ArrayList<SimSlotStatus> slotStatus) {
+            ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> slotStatus) {
         RILRequest rr = mRadioConfig.processResponse(responseInfo);
 
         if (rr != null) {
@@ -64,6 +64,31 @@
     }
 
     /**
+     * Response function for IRadioConfig.getSimSlotsStatus().
+     */
+    public void getSimSlotsStatusResponse_1_2(RadioResponseInfo responseInfo,
+            ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> slotStatus) {
+        RILRequest rr = mRadioConfig.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus_1_2(slotStatus);
+            if (responseInfo.error == RadioError.NONE) {
+                // send response
+                RadioResponse.sendMessageResponse(rr.mResult, ret);
+                Rlog.d(TAG, rr.serialString() + "< "
+                        + mRadioConfig.requestToString(rr.mRequest) + " " + ret.toString());
+            } else {
+                rr.onError(responseInfo.error, ret);
+                Rlog.e(TAG, rr.serialString() + "< "
+                        + mRadioConfig.requestToString(rr.mRequest) + " error "
+                        + responseInfo.error);
+            }
+        } else {
+            Rlog.e(TAG, "getSimSlotsStatusResponse_1_2: Error " + responseInfo.toString());
+        }
+    }
+
+    /**
      * Response function for IRadioConfig.setSimSlotsMapping().
      */
     public void setSimSlotsMappingResponse(RadioResponseInfo responseInfo) {
@@ -86,5 +111,16 @@
         }
     }
 
+    /**
+     * Response function for IRadioConfig.getPhoneCapability().
+     */
+    public void getPhoneCapabilityResponse(RadioResponseInfo info,
+            PhoneCapability phoneCapability) {
+    }
 
+    /**
+     * Response function for IRadioConfig.setPreferredDataModem().
+     */
+    public void setPreferredDataModemResponse(RadioResponseInfo info) {
+    }
 }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index ae76ef3..f2214d6 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -91,6 +91,7 @@
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.SignalStrength;
 import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
@@ -120,13 +121,13 @@
     public void radioStateChanged(int indicationType, int radioState) {
         mRil.processIndication(indicationType);
 
-        CommandsInterface.RadioState newState = getRadioStateFromInt(radioState);
+        int state = getRadioStateFromInt(radioState);
         if (RIL.RILJ_LOGD) {
             mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " +
-                    newState);
+                    state);
         }
 
-        mRil.setRadioState(newState, false /* forceNotifyRegistrants */);
+        mRil.setRadioState(state, false /* forceNotifyRegistrants */);
     }
 
     public void callStateChanged(int indicationType) {
@@ -293,7 +294,10 @@
                     break;
             }
 
-            response.add(new PhysicalChannelConfig(status, config.cellBandwidthDownlink));
+            response.add(new PhysicalChannelConfig.Builder()
+                    .setCellConnectionStatus(status)
+                    .setCellBandwidthDownlinkKhz(config.cellBandwidthDownlink)
+                    .build());
         }
 
         if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
@@ -904,18 +908,22 @@
         mRil.mNattKeepaliveStatusRegistrants.notifyRegistrants(new AsyncResult(null, ks, null));
     }
 
-    private CommandsInterface.RadioState getRadioStateFromInt(int stateInt) {
-        CommandsInterface.RadioState state;
+    /**
+     * @param stateInt
+     * @return {@link TelephonyManager.RadioPowerState RadioPowerState}
+     */
+    private @TelephonyManager.RadioPowerState int getRadioStateFromInt(int stateInt) {
+        int state;
 
         switch(stateInt) {
             case android.hardware.radio.V1_0.RadioState.OFF:
-                state = CommandsInterface.RadioState.RADIO_OFF;
+                state = TelephonyManager.RADIO_POWER_OFF;
                 break;
             case android.hardware.radio.V1_0.RadioState.UNAVAILABLE:
-                state = CommandsInterface.RadioState.RADIO_UNAVAILABLE;
+                state = TelephonyManager.RADIO_POWER_UNAVAILABLE;
                 break;
             case android.hardware.radio.V1_0.RadioState.ON:
-                state = CommandsInterface.RadioState.RADIO_ON;
+                state = TelephonyManager.RADIO_POWER_ON;
                 break;
             default:
                 throw new RuntimeException("Unrecognized RadioState: " + stateInt);
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index dda8b60..af6dcc3 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -113,6 +113,15 @@
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param cardStatus ICC card status as defined by CardStatus in 1.4/types.hal
+     */
+    public void getIccCardStatusResponse_1_4(RadioResponseInfo responseInfo,
+                                             android.hardware.radio.V1_4.CardStatus cardStatus) {
+        responseIccCardStatus_1_4(responseInfo, cardStatus);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
      * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
      */
     public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
@@ -1441,6 +1450,24 @@
         }
     }
 
+    private void responseIccCardStatus_1_4(RadioResponseInfo responseInfo,
+                                           android.hardware.radio.V1_4.CardStatus cardStatus) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            IccCardStatus iccCardStatus = convertHalCardStatus(cardStatus.base.base);
+            iccCardStatus.physicalSlotIndex = cardStatus.base.physicalSlotId;
+            iccCardStatus.atr = cardStatus.base.atr;
+            iccCardStatus.iccid = cardStatus.base.iccid;
+            iccCardStatus.eid = cardStatus.eid;
+            mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, iccCardStatus);
+            }
+            mRil.processResponseDone(rr, responseInfo, iccCardStatus);
+        }
+    }
+
     private void responseInts(RadioResponseInfo responseInfo, int ...var) {
         final ArrayList<Integer> ints = new ArrayList<>();
         for (int i = 0; i < var.length; i++) {
diff --git a/src/java/com/android/internal/telephony/RcsController.java b/src/java/com/android/internal/telephony/RcsController.java
new file mode 100644
index 0000000..e04b4b6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RcsController.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.rcs.RcsManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.rcs.IRcs;
+
+/** Backing implementation of {@link RcsManager}. */
+public class RcsController extends IRcs.Stub {
+    private static final String TAG = "RcsController";
+    private static final String RCS_SERVICE_NAME = "ircs";
+
+    private static RcsController sInstance;
+
+    private final Context mContext;
+
+    /** Initialize the instance. Should only be called once. */
+    public static RcsController init(Context context) {
+        synchronized (RcsController.class) {
+            if (sInstance == null) {
+                sInstance = new RcsController(context);
+            } else {
+                Rlog.e(TAG, "init() called multiple times! sInstance = " + sInstance);
+            }
+        }
+        return sInstance;
+    }
+
+    private RcsController(Context context) {
+        mContext = context;
+        if (ServiceManager.getService(RCS_SERVICE_NAME) == null) {
+            ServiceManager.addService(RCS_SERVICE_NAME, this);
+        }
+    }
+
+    @VisibleForTesting
+    public RcsController(Context context, Void unused) {
+        mContext = context;
+    }
+
+    @Override
+    public void deleteThread(int threadId) {
+        // TODO - add implementation
+    }
+
+    @Override
+    public int getMessageCount(int rcsThreadId) {
+        // TODO - add implementation. Return a magic number for now to test the RPC calls
+        return 1018;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index b2f5fc4..4d91a27 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -21,6 +21,7 @@
 
 import static com.android.internal.telephony.CarrierActionAgent.CARRIER_ACTION_SET_RADIO_ENABLED;
 
+import android.Manifest.permission;
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -76,13 +77,11 @@
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.StatsLog;
-import android.util.TimeUtils;
 import android.util.TimestampedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriInfo;
-import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
@@ -186,6 +185,10 @@
     private boolean mPendingRadioPowerOffAfterDataOff = false;
     private int mPendingRadioPowerOffAfterDataOffTag = 0;
 
+    // This is a flag for debug purposes only. It it set once the RUIM_RECORDS_LOADED event is
+    // received while the phone type is CDMA-LTE, and is never reset after that.
+    private boolean mRuimRecordsLoaded = false;
+
     /** Signal strength poll rate. */
     private static final int POLL_PERIOD_MILLIS = 20 * 1000;
 
@@ -284,6 +287,7 @@
     private final LocalLog mPhoneTypeLog = new LocalLog(10);
     private final LocalLog mRatLog = new LocalLog(20);
     private final LocalLog mRadioPowerLog = new LocalLog(20);
+    private final LocalLog mMdnLog = new LocalLog(20);
 
     private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
         public final AtomicInteger mPreviousSubId =
@@ -441,8 +445,7 @@
                 updateSpnDisplay();
             } else if (intent.getAction().equals(ACTION_RADIO_OFF)) {
                 mAlarmSwitch = false;
-                DcTracker dcTracker = mPhone.mDcTracker;
-                powerOffRadioSafely(dcTracker);
+                powerOffRadioSafely();
             }
         }
     };
@@ -511,7 +514,7 @@
                 .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
         mRestrictedState = new RestrictedState();
 
-        mTransportManager = new TransportManager();
+        mTransportManager = mPhone.getTransportManager();
 
         for (int transportType : mTransportManager.getAvailableTransports()) {
             mRegStateManagers.append(transportType, new NetworkRegistrationManager(
@@ -521,7 +524,7 @@
         }
 
         mLocaleTracker = TelephonyComponentFactory.getInstance().makeLocaleTracker(
-                mPhone, getLooper());
+                mPhone, mNitzState, getLooper());
 
         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
@@ -574,6 +577,7 @@
 
     @VisibleForTesting
     public void updatePhoneType() {
+
         // If we are previously voice roaming, we need to notify that roaming status changed before
         // we change back to non-roaming.
         if (mSS != null && mSS.getVoiceRoaming()) {
@@ -605,10 +609,11 @@
         mStartedGprsRegCheck = false;
         mReportedGprsNoReg = false;
         mMdn = null;
+        logMdnChange("updatePhoneType: setting mMdn to null");
         mMin = null;
         mPrlVersion = null;
         mIsMinInfoReady = false;
-        mNitzState.handleNetworkUnavailable();
+        mNitzState.handleNetworkCountryCodeUnavailable();
         mCellIdentity = null;
         mNewCellIdentity = null;
 
@@ -966,6 +971,7 @@
                     cancelAllNotifications();
                     // clear cached values on SIM removal
                     mMdn = null;
+                    logMdnChange("EVENT_ICC_CHANGED: setting mMdn to null");
                     mMin = null;
                     mIsMinInfoReady = false;
                 }
@@ -1042,7 +1048,8 @@
 
             case EVENT_RADIO_POWER_OFF_DONE:
                 if (DBG) log("EVENT_RADIO_POWER_OFF_DONE");
-                if (mDeviceShuttingDown && mCi.getRadioState().isAvailable()) {
+                if (mDeviceShuttingDown && mCi.getRadioState()
+                        != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                     // during shutdown the modem may not send radio state changed event
                     // as a result of radio power request
                     // Hence, issuing shut down regardless of radio power response
@@ -1066,7 +1073,7 @@
             case EVENT_RADIO_STATE_CHANGED:
             case EVENT_PHONE_TYPE_SWITCHED:
                 if(!mPhone.isPhoneTypeGsm() &&
-                        mCi.getRadioState() == CommandsInterface.RadioState.RADIO_ON) {
+                        mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
                     handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
 
                     // Signal strength polling stops when radio is off.
@@ -1086,7 +1093,7 @@
                 // This callback is called when signal strength is polled
                 // all by itself
 
-                if (!(mCi.getRadioState().isOn())) {
+                if (!(mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON)) {
                     // Polling will continue when radio turns back on
                     return;
                 }
@@ -1313,6 +1320,8 @@
                         String cdmaSubscription[] = (String[]) ar.result;
                         if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
                             mMdn = cdmaSubscription[0];
+                            logMdnChange("EVENT_POLL_STATE_CDMA_SUBSCRIPTION: setting mMdn to "
+                                    + mMdn);
                             parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
 
                             mMin = cdmaSubscription[3];
@@ -1354,17 +1363,25 @@
                         updateSpnDisplay();
                     } else {
                         RuimRecords ruim = (RuimRecords) mIccRecords;
+                        mRuimRecordsLoaded = true;
                         if (ruim != null) {
                             if (ruim.isProvisioned()) {
                                 mMdn = ruim.getMdn();
+                                logMdnChange("EVENT_RUIM_RECORDS_LOADED: setting mMdn to " + mMdn);
                                 mMin = ruim.getMin();
                                 parseSidNid(ruim.getSid(), ruim.getNid());
                                 mPrlVersion = ruim.getPrlVersion();
                                 mIsMinInfoReady = true;
+                            } else {
+                                logMdnChange("EVENT_RUIM_RECORDS_LOADED: ruim not provisioned; "
+                                        + "not updating mMdn " + mMdn);
                             }
                             updateOtaspState();
                             // Notify apps subscription info is ready
                             notifyCdmaSubscriptionInfoReady();
+                        } else {
+                            logMdnChange("EVENT_RUIM_RECORDS_LOADED: ruim is null; "
+                                    + "not updating mMdn " + mMdn);
                         }
                         // SID/NID/PRL is loaded. Poll service state
                         // again to update to the roaming state with
@@ -1497,6 +1514,28 @@
     }
 
     public String getMdnNumber() {
+        // if for CDMA-LTE phone MDN is null, and it has already been updated from RUIM, in some
+        // unknown error scenario mMdn may still have been updated to null. Detect and fix that case
+        if (mMdn == null && mRuimRecordsLoaded && mPhone.isPhoneTypeCdmaLte()) {
+            // query RuimRecords to see if it's not null and the value from there can be used. This
+            // should never be the case except in certain error scenarios/race conditions.
+            RuimRecords ruim = (RuimRecords) mIccRecords;
+            if (ruim != null) {
+                if (ruim.isProvisioned() && ruim.getMdn() != null) {
+                    logeMdnChange("getMdnNumber: mMdn is null when RuimRecords.getMdn() is not");
+
+                    // broadcast intent to indicate an error related to Line1Number has been
+                    // detected
+                    Intent intent = new Intent(TelephonyIntents.ACTION_LINE1_NUMBER_ERROR_DETECTED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                    mPhone.getContext().sendBroadcast(intent,
+                            permission.READ_PRIVILEGED_PHONE_STATE);
+
+                    // update mdn
+                    mMdn = ruim.getMdn();
+                }
+            }
+        }
         return mMdn;
     }
 
@@ -2261,6 +2300,7 @@
 
         String wfcVoiceSpnFormat = null;
         String wfcDataSpnFormat = null;
+        String wfcFlightSpnFormat = null;
         int combinedRegState = getCombinedRegState();
         if (mPhone.getImsPhone() != null && mPhone.getImsPhone().isWifiCallingEnabled()
                 && (combinedRegState == ServiceState.STATE_IN_SERVICE)) {
@@ -2273,6 +2313,7 @@
 
             int voiceIdx = 0;
             int dataIdx = 0;
+            int flightModeIdx = -1;
             boolean useRootLocale = false;
             CarrierConfigManager configLoader = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -2283,6 +2324,8 @@
                         voiceIdx = b.getInt(CarrierConfigManager.KEY_WFC_SPN_FORMAT_IDX_INT);
                         dataIdx = b.getInt(
                                 CarrierConfigManager.KEY_WFC_DATA_SPN_FORMAT_IDX_INT);
+                        flightModeIdx = b.getInt(
+                                CarrierConfigManager.KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT);
                         useRootLocale =
                                 b.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
                     }
@@ -2303,9 +2346,15 @@
                 loge("updateSpnDisplay: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: " + dataIdx);
                 dataIdx = 0;
             }
+            if (flightModeIdx < 0 || flightModeIdx >= wfcSpnFormats.length) {
+                // KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT out of bounds. Use the value from
+                // voiceIdx.
+                flightModeIdx = voiceIdx;
+            }
 
             wfcVoiceSpnFormat = wfcSpnFormats[voiceIdx];
             wfcDataSpnFormat = wfcSpnFormats[dataIdx];
+            wfcFlightSpnFormat = wfcSpnFormats[flightModeIdx];
         }
 
         if (mPhone.isPhoneTypeGsm()) {
@@ -2382,6 +2431,11 @@
                 // Show SPN + Wi-Fi Calling If SIM has SPN and SPN display condition
                 // is satisfied or SPN override is enabled for this carrier.
 
+                // Handle Flight Mode
+                if (mSS.getVoiceRegState() == ServiceState.STATE_POWER_OFF) {
+                    wfcVoiceSpnFormat = wfcFlightSpnFormat;
+                }
+
                 String originalSpn = spn.trim();
                 spn = String.format(wfcVoiceSpnFormat, originalSpn);
                 dataSpn = String.format(wfcDataSpnFormat, originalSpn);
@@ -2448,7 +2502,7 @@
 
                 String originalPlmn = plmn.trim();
                 plmn = String.format(wfcVoiceSpnFormat, originalPlmn);
-            } else if (mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+            } else if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
                 // todo: temporary hack; should have a better fix. This is to avoid using operator
                 // name from ServiceState (populated in resetServiceStateInIwlanMode()) until
                 // wifi calling is actually enabled
@@ -2518,9 +2572,10 @@
 
         // If we want it on and it's off, turn it on
         if (mDesiredPowerState && !mRadioDisabledByCarrier
-                && mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+                && mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
             mCi.setRadioPower(true, null);
-        } else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState().isOn()) {
+        } else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState()
+                == TelephonyManager.RADIO_POWER_ON) {
             // If it's on and available and we want it off gracefully
             if (mPhone.isPhoneTypeGsm() && mPowerOffDelayNeed) {
                 if (mImsRegistrationOnOff && !mAlarmSwitch) {
@@ -2536,14 +2591,13 @@
                     am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                             SystemClock.elapsedRealtime() + 3000, mRadioOffIntent);
                 } else {
-                    DcTracker dcTracker = mPhone.mDcTracker;
-                    powerOffRadioSafely(dcTracker);
+                    powerOffRadioSafely();
                 }
             } else {
-                DcTracker dcTracker = mPhone.mDcTracker;
-                powerOffRadioSafely(dcTracker);
+                powerOffRadioSafely();
             }
-        } else if (mDeviceShuttingDown && mCi.getRadioState().isAvailable()) {
+        } else if (mDeviceShuttingDown
+                && (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE)) {
             mCi.requestShutdown(null);
         }
     }
@@ -2600,6 +2654,16 @@
         mRatLog.log(mSS.toString());
     }
 
+    private void logMdnChange(String msg) {
+        mMdnLog.log(msg);
+        log(msg);
+    }
+
+    private void logeMdnChange(String msg) {
+        mMdnLog.log(msg);
+        loge(msg);
+    }
+
     protected final void log(String s) {
         Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + s);
     }
@@ -2660,7 +2724,7 @@
     }
 
     public boolean isRadioOn() {
-        return mCi.getRadioState() == CommandsInterface.RadioState.RADIO_ON;
+        return mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON;
     }
 
     /**
@@ -2690,19 +2754,19 @@
         log("pollState: modemTriggered=" + modemTriggered);
 
         switch (mCi.getRadioState()) {
-            case RADIO_UNAVAILABLE:
+            case TelephonyManager.RADIO_POWER_UNAVAILABLE:
                 mNewSS.setStateOutOfService();
                 mNewCellIdentity = null;
                 setSignalStrengthDefaultValues();
-                mNitzState.handleNetworkUnavailable();
+                mNitzState.handleNetworkCountryCodeUnavailable();
                 pollStateDone();
                 break;
 
-            case RADIO_OFF:
+            case TelephonyManager.RADIO_POWER_OFF:
                 mNewSS.setStateOff();
                 mNewCellIdentity = null;
                 setSignalStrengthDefaultValues();
-                mNitzState.handleNetworkUnavailable();
+                mNitzState.handleNetworkCountryCodeUnavailable();
                 // don't poll when device is shutting down or the poll was not modemTrigged
                 // (they sent us new radio data) and current network is not IWLAN
                 if (mDeviceShuttingDown ||
@@ -2947,7 +3011,6 @@
 
         if (hasDeregistered) {
             mNetworkDetachedRegistrants.notifyRegistrants();
-            mNitzState.handleNetworkUnavailable();
         }
 
         if (hasRejectCauseChanged) {
@@ -2979,7 +3042,6 @@
                 // operator numeric in locale tracker is null. The async update will allow getting
                 // cell info from the modem instead of using the cached one.
                 mLocaleTracker.updateOperatorNumeric("");
-                mNitzState.handleNetworkUnavailable();
             } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
                 // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
                 // the SIM as if we were talking to towers. Telephony code then uses that with
@@ -2991,28 +3053,6 @@
                 }
 
                 mLocaleTracker.updateOperatorNumeric(operatorNumeric);
-                String countryIsoCode = mLocaleTracker.getCurrentCountry();
-
-                // Update Time Zone.
-                boolean iccCardExists = iccCardExists();
-                boolean networkIsoChanged =
-                        networkCountryIsoChanged(countryIsoCode, prevCountryIsoCode);
-
-                // Determine countryChanged: networkIso is only reliable if there's an ICC card.
-                boolean countryChanged = iccCardExists && networkIsoChanged;
-                if (DBG) {
-                    long ctm = System.currentTimeMillis();
-                    log("Before handleNetworkCountryCodeKnown:"
-                            + " countryChanged=" + countryChanged
-                            + " iccCardExist=" + iccCardExists
-                            + " countryIsoChanged=" + networkIsoChanged
-                            + " operatorNumeric=" + operatorNumeric
-                            + " prevOperatorNumeric=" + prevOperatorNumeric
-                            + " countryIsoCode=" + countryIsoCode
-                            + " prevCountryIsoCode=" + prevCountryIsoCode
-                            + " ltod=" + TimeUtils.logTimeOfDay(ctm));
-                }
-                mNitzState.handleNetworkCountryCodeSet(countryChanged);
             }
 
             tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
@@ -3114,7 +3154,8 @@
 
     private void updateOperatorNameFromEri() {
         if (mPhone.isPhoneTypeCdma()) {
-            if ((mCi.getRadioState().isOn()) && (!mIsSubscriptionFromRuim)) {
+            if ((mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON)
+                    && (!mIsSubscriptionFromRuim)) {
                 String eriText;
                 // Now the Phone sees the new ServiceState so it can get the new ERI text
                 if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
@@ -3130,10 +3171,11 @@
         } else if (mPhone.isPhoneTypeCdmaLte()) {
             boolean hasBrandOverride = mUiccController.getUiccCard(getPhoneId()) != null &&
                     mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() != null;
-            if (!hasBrandOverride && (mCi.getRadioState().isOn()) && (mPhone.isEriFileLoaded()) &&
-                    (!ServiceState.isLte(mSS.getRilVoiceRadioTechnology()) ||
-                            mPhone.getContext().getResources().getBoolean(com.android.internal.R.
-                                    bool.config_LTE_eri_for_network_name))) {
+            if (!hasBrandOverride && (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON)
+                    && (mPhone.isEriFileLoaded())
+                    && (!ServiceState.isLte(mSS.getRilVoiceRadioTechnology())
+                    || mPhone.getContext().getResources().getBoolean(com.android.internal.R
+                    .bool.config_LTE_eri_for_network_name))) {
                 // Only when CDMA is in service, ERI will take effect
                 String eriText = mSS.getOperatorAlpha();
                 // Now the Phone sees the new ServiceState so it can get the new ERI text
@@ -3641,7 +3683,7 @@
         CarrierConfigManager configManager = (CarrierConfigManager)
                 context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager != null) {
-            PersistableBundle bundle = configManager.getConfig();
+            PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
             if (bundle != null) {
                 boolean disableVoiceBarringNotification = bundle.getBoolean(
                         CarrierConfigManager.KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
@@ -4018,18 +4060,13 @@
      *
      * Hang up the existing voice calls to decrease call drop rate.
      */
-    public void powerOffRadioSafely(DcTracker dcTracker) {
+    public void powerOffRadioSafely() {
         synchronized (this) {
             if (!mPendingRadioPowerOffAfterDataOff) {
-                int dds = SubscriptionManager.getDefaultDataSubscriptionId();
                 // To minimize race conditions we call cleanUpAllConnections on
                 // both if else paths instead of before this isDisconnected test.
-                if (dcTracker.isDisconnected()
-                        && (dds == mPhone.getSubId()
-                        || (dds != mPhone.getSubId()
-                        && ProxyController.getInstance().isDataDisconnected(dds)))) {
+                if (mPhone.areAllDataDisconnected()) {
                     // To minimize race conditions we do this after isDisconnected
-                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
                     if (DBG) log("Data disconnected, turn off radio right away.");
                     hangupAndPowerOff();
                 } else {
@@ -4039,15 +4076,18 @@
                         mPhone.mCT.mBackgroundCall.hangupIfAlive();
                         mPhone.mCT.mForegroundCall.hangupIfAlive();
                     }
-                    if (!ProxyController.getInstance().isDataDisconnected(mPhone.getSubId())) {
-                        if (DBG) log("Wait for all data disconnect");
-                        // Data is not disconnected. Wait for the data disconnect complete
-                        // before sending the RADIO_POWER off.
-                        ProxyController.getInstance().registerForAllDataDisconnected(
-                                mPhone.getSubId(), this, EVENT_ALL_DATA_DISCONNECTED, null);
-                        mPendingRadioPowerOffAfterDataOff = true;
+                    if (DBG) log("Wait for all data disconnect");
+                    // Data is not disconnected. Wait for the data disconnect complete
+                    // before sending the RADIO_POWER off.
+                    ProxyController.getInstance().registerForAllDataDisconnected(
+                            mPhone.getSubId(), this, EVENT_ALL_DATA_DISCONNECTED);
+                    mPendingRadioPowerOffAfterDataOff = true;
+                    for (int transport : mTransportManager.getAvailableTransports()) {
+                        if (mPhone.getDcTracker(transport) != null) {
+                            mPhone.getDcTracker(transport).cleanUpAllConnections(
+                                    Phone.REASON_RADIO_TURNED_OFF);
+                        }
                     }
-                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
 
                     Message msg = Message.obtain(this);
                     msg.what = EVENT_SET_RADIO_POWER_OFF;
@@ -4551,6 +4591,11 @@
         ipw.println(" Radio power Log:");
         ipw.increaseIndent();
         mRadioPowerLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+
+        ipw.println(" mMdn Log:");
+        ipw.increaseIndent();
+        mMdnLog.dump(fd, ipw, args);
 
         mNitzState.dumpLogs(fd, ipw, args);
     }
@@ -4730,7 +4775,7 @@
      * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF
      */
     protected void resetServiceStateInIwlanMode() {
-        if (mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+        if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
             boolean resetIwlanRatVal = false;
             log("set service state as POWER_OFF");
             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 8c08420..09fc531 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -63,6 +65,7 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
@@ -300,8 +303,8 @@
                 SubscriptionManager.MNC_STRING));
         String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.CARD_ID));
-        // FIXME: consider stick this into database too
-        String countryIso = getSubscriptionCountryIso(id);
+        String countryIso = cursor.getString(cursor.getColumnIndexOrThrow(
+                SubscriptionManager.ISO_COUNTRY_CODE));
         boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.IS_EMBEDDED)) == 1;
         UiccAccessRule[] accessRules;
@@ -313,8 +316,10 @@
         }
         boolean isOpportunistic = cursor.getInt(cursor.getColumnIndexOrThrow(
                 SubscriptionManager.IS_OPPORTUNISTIC)) == 1;
-        int parentSubId = cursor.getInt(cursor.getColumnIndexOrThrow(
-                SubscriptionManager.PARENT_SUB_ID));
+        String groupUUID = cursor.getString(cursor.getColumnIndexOrThrow(
+                SubscriptionManager.GROUP_UUID));
+        boolean isMetered = cursor.getInt(cursor.getColumnIndexOrThrow(
+                SubscriptionManager.IS_METERED)) == 1;
 
         if (VDBG) {
             String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
@@ -325,7 +330,7 @@
                     + " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso + " isEmbedded:"
                     + isEmbedded + " accessRules:" + Arrays.toString(accessRules)
                     + " cardId:" + cardIdToPrint + " isOpportunistic:" + isOpportunistic
-                    + " parentSubId:" + parentSubId);
+                    + " groupUUID:" + groupUUID + " isMetered:" + isMetered);
         }
 
         // If line1number has been set to a different number, use it instead.
@@ -335,21 +340,7 @@
         }
         return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                 nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                isEmbedded, accessRules, cardId, isOpportunistic, parentSubId);
-    }
-
-    /**
-     * Get ISO country code for the subscription's provider
-     *
-     * @param subId The subscription ID
-     * @return The ISO country code for the subscription's provider
-     */
-    private String getSubscriptionCountryIso(int subId) {
-        final int phoneId = getPhoneId(subId);
-        if (phoneId < 0) {
-            return "";
-        }
-        return mTelephonyManager.getSimCountryIsoForPhone(phoneId);
+                isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered);
     }
 
     /**
@@ -1383,6 +1374,27 @@
         return result;
     }
 
+    /**
+     * Set ISO country code by subscription ID
+     * @param iso iso country code associated with the subscription
+     * @param subId the unique SubInfoRecord index in database
+     * @return the number of records updated
+     */
+    public int setCountryIso(String iso, int subId) {
+        if (DBG) logd("[setCountryIso]+ iso:" + iso + " subId:" + subId);
+        ContentValues value = new ContentValues();
+        value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso);
+
+        int result = mContext.getContentResolver().update(
+                SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+
+        // Refresh the Cache of Active Subscription Info List
+        refreshCachedActiveSubscriptionInfoList();
+
+        notifySubscriptionInfoChanged();
+        return result;
+    }
+
     @Override
     public int getSlotIndex(int subId) {
         if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId);
@@ -1447,10 +1459,10 @@
         int size = sSlotIndexToSubId.size();
         if (size == 0) {
             if (VDBG) {
-                logd("[getSubId]- sSlotIndexToSubId.size == 0, return DummySubIds slotIndex="
+                logd("[getSubId]- sSlotIndexToSubId.size == 0, return null slotIndex="
                         + slotIndex);
             }
-            return getDummySubIds(slotIndex);
+            return null;
         }
 
         // Create an array of subIds that are in this slot?
@@ -1473,8 +1485,8 @@
             if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
             return subIdArr;
         } else {
-            if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIndex=" + slotIndex);
-            return getDummySubIds(slotIndex);
+            if (DBG) logd("[getSubId]- numSubIds == 0, return null slotIndex=" + slotIndex);
+            return null;
         }
     }
 
@@ -1522,27 +1534,6 @@
 
     }
 
-    private int[] getDummySubIds(int slotIndex) {
-        // FIXME: Remove notion of Dummy SUBSCRIPTION_ID.
-        // I tested this returning null as no one appears to care,
-        // but no connection came up on sprout with two sims.
-        // We need to figure out why and hopefully remove DummySubsIds!!!
-        int numSubs = getActiveSubInfoCountMax();
-        if (numSubs > 0) {
-            int[] dummyValues = new int[numSubs];
-            for (int i = 0; i < numSubs; i++) {
-                dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIndex;
-            }
-            if (VDBG) {
-                logd("getDummySubIds: slotIndex=" + slotIndex
-                    + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
-            }
-            return dummyValues;
-        } else {
-            return null;
-        }
-    }
-
     /**
      * @return the number of records cleared
      */
@@ -2046,7 +2037,7 @@
             case SubscriptionManager.CB_OPT_OUT_DIALOG:
             case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
             case SubscriptionManager.IS_OPPORTUNISTIC:
-            case SubscriptionManager.PARENT_SUB_ID:
+            case SubscriptionManager.IS_METERED:
             case SubscriptionManager.VT_IMS_ENABLED:
             case SubscriptionManager.WFC_IMS_ENABLED:
             case SubscriptionManager.WFC_IMS_MODE:
@@ -2106,7 +2097,8 @@
                         case SubscriptionManager.WFC_IMS_ROAMING_MODE:
                         case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
                         case SubscriptionManager.IS_OPPORTUNISTIC:
-                        case SubscriptionManager.PARENT_SUB_ID:
+                        case SubscriptionManager.GROUP_UUID:
+                        case SubscriptionManager.IS_METERED:
                             resultValue = cursor.getInt(0) + "";
                             break;
                         default:
@@ -2259,52 +2251,28 @@
     }
 
     /**
-     * Set parent subId (parentSubId) of another subscription (subId).
-     * It's used in scenarios where a child ubscription is bundled with a
-     * primary parent subscription. The child subscription will typically be opportunistic
-     * and will be used to provide data services where available, with the parent being
-     * the primary fallback subscription.
+     * Set whether a subscription is metered
      *
-     * @param parentSubId subId of its parent subscription.
+     * @param isMetered whether it’s a metered subscription.
      * @param subId the unique SubscriptionInfo index in database
      * @return the number of records updated
      */
     @Override
-    public int setParentSubId(int parentSubId, int subId) {
-        enforceModifyPhoneState("setParentSubId");
-        final long token = Binder.clearCallingIdentity();
-
-        try {
-            if (!SubscriptionManager.isUsableSubIdValue(parentSubId)
-                    || parentSubId > getAllSubInfoCount(mContext.getOpPackageName())
-                    || parentSubId == subId) {
-                if (DBG) {
-                    logd("[setParentSubId]- fail with parentSubId " + parentSubId
-                            + " subId " + subId);
-                }
-                return -1;
-            }
-
-            return setSubscriptionProperty(subId, SubscriptionManager.PARENT_SUB_ID,
-                    String.valueOf(parentSubId));
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+    public int setMetered(boolean isMetered, int subId) {
+        return setSubscriptionProperty(subId, SubscriptionManager.IS_METERED,
+                String.valueOf(isMetered ? 1 : 0));
     }
 
     @Override
-    public int setPreferredData(int slotId) {
+    public int setPreferredData(int subId) {
         enforceModifyPhoneState("setPreferredData");
         final long token = Binder.clearCallingIdentity();
 
         try {
-            // TODO: make this API takes in subId directly.
-            int subId = getSubIdUsingPhoneId(slotId);
-
             if (mPreferredDataSubId != subId) {
                 mPreferredDataSubId = subId;
                 PhoneSwitcher.getInstance().setPreferredData(subId);
-                //TODO: notifyPreferredDataSubIdChanged();
+                notifyPreferredDataSubIdChanged();
             }
 
             return 0;
@@ -2313,12 +2281,125 @@
         }
     }
 
+    private void notifyPreferredDataSubIdChanged() {
+        ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+                "telephony.registry"));
+        try {
+            if (DBG) logd("notifyPreferredDataSubIdChanged:");
+            tr.notifyPreferredDataSubIdChanged(mPreferredDataSubId);
+        } catch (RemoteException ex) {
+            // Should never happen because its always available.
+        }
+    }
+
     @Override
-    public List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId, String callingPackage) {
+    public List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage) {
         return getSubscriptionInfoListFromCacheHelper(
                 callingPackage, mCacheOpportunisticSubInfoList);
     }
 
+    /**
+     * 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.
+     *
+     * @return groupUUID a UUID assigned to the subscription group. It returns
+     * null if fails.
+     *
+     */
+    @Override
+    public String setSubscriptionGroup(int[] subIdList, String callingPackage) {
+        boolean hasModifyPermission = mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE) == PERMISSION_GRANTED;
+
+        // If caller doesn't have modify permission or carrier privilege permission on certain
+        // subscriptions, maybe because the they are not active. So we keep them in a hashset and
+        // later check access rules in our database to know whether they can manage them.
+        Set<Integer> subIdCheckList = new HashSet<>();
+        for (int subId : subIdList) {
+            if (!mTelephonyManager.hasCarrierPrivileges(subId)) {
+                subIdCheckList.add(subId);
+            }
+        }
+
+        long identity = Binder.clearCallingIdentity();
+
+        try {
+            if (!isSubInfoReady()) {
+                if (DBG) logdl("[getSubscriptionInfoList] Sub Controller not ready");
+                return null;
+            }
+
+            SubscriptionManager subscriptionManager = (SubscriptionManager)
+                    mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+            List<SubscriptionInfo> subList = getSubInfo(null, null);
+
+            for (SubscriptionInfo subInfo : subList) {
+                if (subIdCheckList.contains(subInfo.getSubscriptionId())) {
+                    // If caller doesn't have modify permission or privilege access to
+                    // the subscription, operation is invalid and returns null.
+                    if (hasModifyPermission || (subInfo.isEmbedded()
+                            && subscriptionManager.canManageSubscription(
+                                    subInfo, callingPackage))) {
+                        subIdCheckList.remove(subInfo.getSubscriptionId());
+                    } else {
+                        if (DBG) {
+                            logdl("setSubscriptionGroup doesn't have permission on"
+                                    + " subInfo " + subInfo);
+                        }
+                        return null;
+                    }
+                }
+            }
+
+            if (!subIdCheckList.isEmpty()) {
+                // Some SubId not found.
+                StringBuilder subIdNotFound = new StringBuilder();
+                for (int subId : subIdCheckList) {
+                    subIdNotFound.append(subId + " ");
+                }
+                if (DBG) {
+                    logdl("setSubscriptionGroup subId not existed: "
+                            + subIdNotFound.toString());
+                }
+
+                return null;
+            }
+
+            // Generate a UUID.
+            String groupUUID = UUID.randomUUID().toString();
+
+            // Selection should be: "in (subId1, subId2, ...)".
+            StringBuilder selection = new StringBuilder();
+            selection.append(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID);
+            selection.append(" IN (");
+            for (int i = 0; i < subIdList.length - 1; i++) {
+                selection.append(subIdList[i] + ", ");
+            }
+            selection.append(subIdList[subIdList.length - 1]);
+            selection.append(")");
+            ContentValues value = new ContentValues();
+            value.put(SubscriptionManager.GROUP_UUID, groupUUID);
+            int result = mContext.getContentResolver().update(
+                    SubscriptionManager.CONTENT_URI, value, selection.toString(), null);
+
+            if (DBG) logdl("setSubscriptionGroup update DB result: " + result);
+
+            refreshCachedActiveSubscriptionInfoList();
+
+            return groupUUID;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     // Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
     // They are doing similar things except operating on different cache.
     private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
@@ -2349,7 +2430,7 @@
                         try {
                             return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
                                     subscriptionInfo.getSubscriptionId(), callingPackage,
-                                    "getOpportunisticSubscriptions");
+                                    "getSubscriptionInfoList");
                         } catch (SecurityException e) {
                             return false;
                         }
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 1ee96a8..4bf8638 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -122,6 +122,13 @@
 
     public SubscriptionInfoUpdater(
             Looper looper, Context context, Phone[] phone, CommandsInterface[] ci) {
+        this(looper, context, phone, ci,
+                IPackageManager.Stub.asInterface(ServiceManager.getService("package")));
+    }
+
+    @VisibleForTesting public SubscriptionInfoUpdater(
+            Looper looper, Context context, Phone[] phone,
+            CommandsInterface[] ci, IPackageManager packageMgr) {
         super(looper);
         logd("Constructor invoked");
 
@@ -129,7 +136,7 @@
         mPhone = phone;
         mSubscriptionManager = SubscriptionManager.from(mContext);
         mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
-        mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        mPackageManager = packageMgr;
 
         mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
         initializeCarrierApps();
@@ -236,10 +243,11 @@
                 break;
 
             case EVENT_SIM_UNKNOWN:
-                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
                 broadcastSimStateChanged(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null);
                 broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
                 broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_UNKNOWN);
+                updateSubscriptionCarrierId(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
+                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
                 break;
 
             case EVENT_SIM_IO_ERROR:
@@ -247,12 +255,14 @@
                 break;
 
             case EVENT_SIM_RESTRICTED:
-                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
                 broadcastSimStateChanged(msg.arg1,
                         IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED,
                         IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
                 broadcastSimCardStateChanged(msg.arg1, TelephonyManager.SIM_STATE_CARD_RESTRICTED);
                 broadcastSimApplicationStateChanged(msg.arg1, TelephonyManager.SIM_STATE_NOT_READY);
+                updateSubscriptionCarrierId(msg.arg1,
+                        IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
+                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED);
                 break;
 
             case EVENT_SIM_READY:
@@ -326,10 +336,11 @@
             updateSubscriptionInfoByIccId();
         }
 
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
         broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
         broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_PRESENT);
         broadcastSimApplicationStateChanged(slotId, getSimStateFromLockedReason(reason));
+        updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
     }
 
     private static int getSimStateFromLockedReason(String lockedReason) {
@@ -389,6 +400,14 @@
                     logd("EVENT_RECORDS_LOADED Operator name is null");
                 }
 
+                String iso = tm.getSimCountryIsoForPhone(slotId);
+
+                if (!TextUtils.isEmpty(iso)) {
+                    SubscriptionController.getInstance().setCountryIso(iso, subId);
+                } else {
+                    logd("EVENT_RECORDS_LOADED sim country iso is null");
+                }
+
                 String msisdn = tm.getLine1Number(subId);
                 ContentResolver contentResolver = mContext.getContentResolver();
 
@@ -460,9 +479,18 @@
                 mPackageManager, TelephonyManager.getDefault(),
                 mContext.getContentResolver(), mCurrentlyActiveUserId);
 
+        /**
+         * The sim loading sequence will be
+         *  1. ACTION_SUBINFO_CONTENT_CHANGE happens through updateSubscriptionInfoByIccId() above.
+         *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
+         *  /ACTION_SIM_APPLICATION_STATE_CHANGED
+         *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
+         *  4. ACTION_CARRIER_CONFIG_CHANGED
+         */
         broadcastSimStateChanged(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
         broadcastSimCardStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_PRESENT);
         broadcastSimApplicationStateChanged(loadedSlotId, TelephonyManager.SIM_STATE_LOADED);
+        updateSubscriptionCarrierId(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
         updateCarrierServices(loadedSlotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
     }
 
@@ -473,6 +501,12 @@
         mCarrierServiceBindHelper.updateForPhoneId(slotId, simState);
     }
 
+    private void updateSubscriptionCarrierId(int slotId, String simState) {
+        if (mPhone != null && mPhone[slotId] != null) {
+            mPhone[slotId].resolveSubscriptionCarrierId(simState);
+        }
+    }
+
     private void handleSimAbsent(int slotId) {
         if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
             logd("SIM" + (slotId + 1) + " hot plug out");
@@ -481,10 +515,11 @@
         if (isAllIccIdQueryDone()) {
             updateSubscriptionInfoByIccId();
         }
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
         broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
         broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_ABSENT);
         broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
+        updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
     }
 
     private void handleSimError(int slotId) {
@@ -495,11 +530,12 @@
         if (isAllIccIdQueryDone()) {
             updateSubscriptionInfoByIccId();
         }
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
         broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
                 IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
         broadcastSimCardStateChanged(slotId, TelephonyManager.SIM_STATE_CARD_IO_ERROR);
         broadcastSimApplicationStateChanged(slotId, TelephonyManager.SIM_STATE_NOT_READY);
+        updateSubscriptionCarrierId(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 31abc53..4522d8a 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -22,12 +22,11 @@
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.ServiceManager;
-import android.telephony.AccessNetworkConstants.TransportType;
 
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
-import com.android.internal.telephony.dataconnection.AccessNetworksManager;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
@@ -85,8 +84,8 @@
         return new SimActivationTracker(phone);
     }
 
-    public DcTracker makeDcTracker(Phone phone) {
-        return new DcTracker(phone, TransportType.WWAN);
+    public DcTracker makeDcTracker(Phone phone, int transportType) {
+        return new DcTracker(phone, transportType);
     }
 
     public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
@@ -97,8 +96,8 @@
         return new CarrierActionAgent(phone);
     }
 
-    public CarrierIdentifier makeCarrierIdentifier(Phone phone) {
-        return new CarrierIdentifier(phone);
+    public CarrierResolver makeCarrierResolver(Phone phone) {
+        return new CarrierResolver(phone);
     }
 
     public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) {
@@ -172,8 +171,8 @@
         return new DeviceStateMonitor(phone);
     }
 
-    public AccessNetworksManager makeAccessNetworksManager(Phone phone) {
-        return new AccessNetworksManager(phone);
+    public TransportManager makeTransportManager(Phone phone) {
+        return new TransportManager(phone);
     }
 
     public CdmaSubscriptionSourceManager
@@ -186,8 +185,8 @@
         return IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
     }
-
-    public LocaleTracker makeLocaleTracker(Phone phone, Looper looper) {
-        return new LocaleTracker(phone, looper);
+    public LocaleTracker makeLocaleTracker(Phone phone, NitzStateMachine nitzStateMachine,
+                                           Looper looper) {
+        return new LocaleTracker(phone, nitzStateMachine, looper);
     }
 }
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index bb7ac00..0ab6ec8 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -111,6 +111,13 @@
     private static final String ACTION_TEST_IMS_E_CALL =
             "com.android.internal.telephony.TestImsECall";
 
+    /**
+     * Test-only intent used to trigger a change to the current call's phone number.
+     * Use the {@link #EXTRA_NUMBER} extra to specify the new phone number.
+     */
+    private static final String ACTION_TEST_CHANGE_NUMBER =
+            "com.android.internal.telephony.TestChangeNumber";
+
     private static final String ACTION_TEST_SERVICE_STATE =
             "com.android.internal.telephony.TestServiceState";
 
@@ -171,6 +178,9 @@
                 } else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
                     log("handle test IMS ecall intent");
                     testImsECall();
+                } else if (action.equals(ACTION_TEST_CHANGE_NUMBER)) {
+                    log("handle test change number intent");
+                    testChangeNumber(intent);
                 } else {
                     if (DBG) log("onReceive: unknown action=" + action);
                 }
@@ -205,7 +215,7 @@
                 filter.addAction(ACTION_TEST_SERVICE_STATE);
                 log("register for intent action=" + ACTION_TEST_SERVICE_STATE);
             }
-
+            filter.addAction(ACTION_TEST_CHANGE_NUMBER);
             phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
         }
     }
@@ -414,4 +424,29 @@
         imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
                 callProfile);
     }
+
+    void testChangeNumber(Intent intent) {
+        if (!intent.hasExtra(EXTRA_NUMBER)) {
+            return;
+        }
+
+        String newNumber = intent.getStringExtra(EXTRA_NUMBER);
+
+        // Update all the calls.
+        mPhone.getForegroundCall().getConnections()
+                .stream()
+                .forEach(c -> {
+                    c.setAddress(newNumber, PhoneConstants.PRESENTATION_ALLOWED);
+                    c.setDialString(newNumber);
+                });
+
+        // <sigh>
+        if (mPhone instanceof GsmCdmaPhone) {
+            ((GsmCdmaPhone) mPhone).notifyPhoneStateChanged();
+            ((GsmCdmaPhone) mPhone).notifyPreciseCallStateChanged();
+        } else if (mPhone instanceof ImsPhone) {
+            ((ImsPhone) mPhone).notifyPhoneStateChanged();
+            ((ImsPhone) mPhone).notifyPreciseCallStateChanged();
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
index 101fddd..a0f7447 100644
--- a/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
+++ b/src/java/com/android/internal/telephony/TimeZoneLookupHelper.java
@@ -18,8 +18,8 @@
 
 import android.text.TextUtils;
 
-import libcore.util.CountryTimeZones;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.CountryTimeZones;
+import libcore.timezone.TimeZoneFinder;
 
 import java.util.Date;
 import java.util.TimeZone;
diff --git a/src/java/com/android/internal/telephony/TransportManager.java b/src/java/com/android/internal/telephony/TransportManager.java
index d81130a..a093b00 100644
--- a/src/java/com/android/internal/telephony/TransportManager.java
+++ b/src/java/com/android/internal/telephony/TransportManager.java
@@ -16,28 +16,170 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
 import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.data.ApnSetting.ApnType;
+import android.text.TextUtils;
 
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks;
+import com.android.internal.util.IndentingPrintWriter;
+
+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.List;
 
 /**
- * This class represents the transport manager which manages available transports and
- * route requests to correct transport.
+ * 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.
  */
-public class TransportManager {
+public class TransportManager extends Handler {
     private static final String TAG = TransportManager.class.getSimpleName();
 
-    private List<Integer> mAvailableTransports = new ArrayList<>();
+    private static final boolean DBG = true;
 
-    public TransportManager() {
-        // TODO: get transpot list from AccessNetworkManager.
-        mAvailableTransports.add(TransportType.WWAN);
+    private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1;
+
+    public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE =
+            "ro.telephony.iwlan_operation_mode";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"IWLAN_OPERATION_MODE_"},
+            value = {
+                    IWLAN_OPERATION_MODE_DEFAULT,
+                    IWLAN_OPERATION_MODE_LEGACY,
+                    IWLAN_OPERATION_MODE_AP_ASSISTED})
+    public @interface IwlanOperationMode {}
+
+    /**
+     * IWLAN default mode. On device that has IRadio 1.3 or above, it means
+     * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.2 or below, it means
+     * {@link #IWLAN_OPERATION_MODE_LEGACY}.
+     */
+    public static final int IWLAN_OPERATION_MODE_DEFAULT = 0;
+
+    /**
+     * IWLAN legacy mode. IWLAN is completely handled by the modem, and when the device is on
+     * IWLAN, modem reports IWLAN as a RAT.
+     */
+    public static final int IWLAN_OPERATION_MODE_LEGACY = 1;
+
+    /**
+     * IWLAN application processor assisted mode. IWLAN is handled by the bound IWLAN data service
+     * and network service separately.
+     */
+    public static final int IWLAN_OPERATION_MODE_AP_ASSISTED = 2;
+
+    private final Phone mPhone;
+
+    /** The available transports. Must be one or more of AccessNetworkConstants.TransportType.XXX */
+    private final int[] mAvailableTransports;
+
+    private final AccessNetworksManager mAccessNetworksManager;
+
+    public TransportManager(Phone phone) {
+        mPhone = phone;
+        mAccessNetworksManager = new AccessNetworksManager(phone);
+
+        mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
+                EVENT_QUALIFIED_NETWORKS_CHANGED);
+
+        if (isInLegacyMode()) {
+            // For legacy mode, WWAN is the only transport to handle all data connections, even
+            // the IWLAN ones.
+            mAvailableTransports = new int[]{TransportType.WWAN};
+        } else {
+            mAvailableTransports = new int[]{TransportType.WWAN, TransportType.WLAN};
+        }
     }
 
-    public List<Integer> getAvailableTransports() {
-        return new ArrayList<>(mAvailableTransports);
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_QUALIFIED_NETWORKS_CHANGED:
+                AsyncResult ar = (AsyncResult) msg.obj;
+                List<QualifiedNetworks> networks = (List<QualifiedNetworks>) ar.result;
+                updateAvailableNetworks(networks);
+                break;
+            default:
+                loge("Unexpected event " + msg.what);
+                break;
+        }
+    }
+
+    private synchronized void updateAvailableNetworks(List<QualifiedNetworks> networks) {
+        log("updateAvailableNetworks: " + networks);
+        //TODO: Update available networks and transports.
+    }
+
+    /**
+     * @return The available transports. Note that on legacy devices, the only available transport
+     * would be WWAN only. If the device is configured as AP-assisted mode, the available transport
+     * will always be WWAN and WLAN (even if the device is not camped on IWLAN).
+     * See {@link #isInLegacyMode()} for mode details.
+     */
+    public synchronized @NonNull int[] getAvailableTransports() {
+        return mAvailableTransports;
+    }
+
+    /**
+     * @return True if in IWLAN legacy mode. Operating in legacy mode means telephony will send
+     * all data requests to the default data service, which is the cellular data service.
+     * AP-assisted mode requires properly configuring the resource overlay
+     * 'config_wwan_data_service_package' (or the carrier config
+     * {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING }) to
+     * the IWLAN data service package, 'config_wwan_network_service_package' (or the carrier config
+     * {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING })
+     * to the IWLAN network service package, and 'config_qualified_networks_service_package' (or the
+     * carrier config
+     * {@link CarrierConfigManager#KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING})
+     * to the qualified networks service package.
+     */
+    public boolean isInLegacyMode() {
+        return (mPhone.mCi.getIwlanOperationMode() == IWLAN_OPERATION_MODE_LEGACY);
+    }
+
+    /**
+     * Get the corresponding transport based on the APN type
+     *
+     * @param apnType APN type
+     * @return The transport type
+     */
+    public int getCurrentTransport(@ApnType int apnType) {
+        // TODO: Look up the transport from the transport type map
+        return TransportType.WWAN;
+    }
+
+    /**
+     * 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("TransportManager:");
+        pw.increaseIndent();
+        pw.print("mAvailableTransports=");
+        List<String> transportsStrings = new ArrayList<>();
+        for (int i = 0; i < mAvailableTransports.length; i++) {
+            transportsStrings.add(TransportType.toString(mAvailableTransports[i]));
+        }
+        pw.println("[" + TextUtils.join(",", transportsStrings) + "]");
+        mAccessNetworksManager.dump(fd, pw, args);
+        pw.decreaseIndent();
+        pw.flush();
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 6ff1c39..a38cc8b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -29,6 +29,7 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.data.ApnSetting;
@@ -40,8 +41,12 @@
 import android.util.SparseArray;
 
 import com.android.internal.telephony.Phone;
+import com.android.internal.util.IndentingPrintWriter;
 
+import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Access network manager manages the qualified/available networks for mobile data connection.
@@ -52,6 +57,16 @@
     private static final String TAG = AccessNetworksManager.class.getSimpleName();
     private static final boolean DBG = false;
 
+    private static final int[] SUPPORTED_APN_TYPES = {
+            ApnSetting.TYPE_DEFAULT,
+            ApnSetting.TYPE_MMS,
+            ApnSetting.TYPE_FOTA,
+            ApnSetting.TYPE_IMS,
+            ApnSetting.TYPE_CBS,
+            ApnSetting.TYPE_SUPL,
+            ApnSetting.TYPE_EMERGENCY
+    };
+
     private final Phone mPhone;
 
     private final CarrierConfigManager mCarrierConfigManager;
@@ -88,13 +103,25 @@
      * Represents qualified network types list on a specific APN type.
      */
     public static class QualifiedNetworks {
-        @ApnType
-        public final int apnType;
+        public final @ApnType int apnType;
         public final int[] qualifiedNetworks;
         public QualifiedNetworks(@ApnType int apnType, int[] qualifiedNetworks) {
             this.apnType = apnType;
             this.qualifiedNetworks = qualifiedNetworks;
         }
+
+        @Override
+        public String toString() {
+            List<String> accessNetworkStrings = new ArrayList<>();
+            for (int network : qualifiedNetworks) {
+                accessNetworkStrings.add(AccessNetworkType.toString(network));
+            }
+            return "[QualifiedNetworks: apnType="
+                    + ApnSetting.getApnTypeString(apnType)
+                    + ", networks="
+                    + TextUtils.join(", ", accessNetworkStrings)
+                    + "]";
+        }
     }
 
     private class AccessNetworksManagerDeathRecipient implements IBinder.DeathRecipient {
@@ -132,24 +159,34 @@
     private final class QualifiedNetworksServiceCallback extends
             IQualifiedNetworksServiceCallback.Stub {
         @Override
-        public void onQualifiedNetworkTypesChanged(int apnType, int[] qualifiedNetworkTypesList) {
-            log("onQualifiedNetworkTypesChanged. apnType = "
-                    + ApnSetting.getApnTypesStringFromBitmask(apnType)
-                    + ", networks = " + Arrays.toString(qualifiedNetworkTypesList));
-
-            // TODO: Verify the preference from data settings manager to make sure the order
-            // of the networks do not violate users/carrier's preference.
-            if (mAvailableNetworks.get(apnType) != null) {
-                if (Arrays.equals(mAvailableNetworks.get(apnType), qualifiedNetworkTypesList)) {
-                    log("Available networks for "
-                            + ApnSetting.getApnTypesStringFromBitmask(apnType) + " not changed.");
-                    return;
+        public void onQualifiedNetworkTypesChanged(int apnTypes, int[] qualifiedNetworkTypes) {
+            log("onQualifiedNetworkTypesChanged. apnTypes = "
+                    + ApnSetting.getApnTypesStringFromBitmask(apnTypes)
+                    + ", networks = " + Arrays.toString(qualifiedNetworkTypes));
+            List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
+            for (int supportedApnType : SUPPORTED_APN_TYPES) {
+                if ((apnTypes & supportedApnType) == supportedApnType) {
+                    // TODO: Verify the preference from data settings manager to make sure the order
+                    // of the networks do not violate users/carrier's preference.
+                    if (mAvailableNetworks.get(supportedApnType) != null) {
+                        if (Arrays.equals(mAvailableNetworks.get(supportedApnType),
+                                qualifiedNetworkTypes)) {
+                            log("Available networks for "
+                                    + ApnSetting.getApnTypesStringFromBitmask(supportedApnType)
+                                    + " not changed.");
+                            continue;
+                        }
+                    }
+                    mAvailableNetworks.put(supportedApnType, qualifiedNetworkTypes);
+                    qualifiedNetworksList.add(new QualifiedNetworks(supportedApnType,
+                            qualifiedNetworkTypes));
                 }
             }
-            mAvailableNetworks.put(apnType, qualifiedNetworkTypesList);
-            mQualifiedNetworksChangedRegistrants.notifyRegistrants(
-                    new AsyncResult(null,
-                            new QualifiedNetworks(apnType, qualifiedNetworkTypesList), null));
+
+            if (!qualifiedNetworksList.isEmpty()) {
+                mQualifiedNetworksChangedRegistrants.notifyRegistrants(
+                        new AsyncResult(null, qualifiedNetworksList, null));
+            }
         }
     }
 
@@ -266,12 +303,29 @@
     }
 
     /**
-     * @return True if IWLAN legacy mode is used. No qualified network service there to provide
-     * information for platform to setup data connection. All data connection requests will be
-     * routed to the default (i.e. cellular) data/network service.
+     * Dump the state of transport manager
+     *
+     * @param fd File descriptor
+     * @param pw Print writer
+     * @param args Arguments
      */
-    public boolean isInLegacyMode() {
-        return TextUtils.isEmpty(getQualifiedNetworksServicePackageName());
+    public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
+        pw.println("AccessNetworksManager:");
+        pw.increaseIndent();
+        pw.println("Available networks:");
+        pw.increaseIndent();
+
+        for (int i = 0; i < mAvailableNetworks.size(); i++) {
+            pw.print("APN type "
+                    + ApnSetting.getApnTypeString(mAvailableNetworks.keyAt(i)) + ": ");
+            List<String> networksStrings = new ArrayList<>();
+            for (int network : mAvailableNetworks.valueAt(i)) {
+                networksStrings.add(AccessNetworkType.toString(network));
+            }
+            pw.println("[" + TextUtils.join(",", networksStrings) + "]");
+        }
+        pw.decreaseIndent();
+        pw.decreaseIndent();
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 0f57e46..fa13dac 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -460,7 +460,11 @@
         }
     }
 
-    public boolean hasNoRestrictedRequests(boolean excludeDun) {
+    /**
+     * @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 &&
@@ -468,13 +472,13 @@
                         NetworkCapabilities.NET_CAPABILITY_DUN)) {
                     continue;
                 }
-                if (nr.networkCapabilities.hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) {
-                    return false;
+                if (!nr.networkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
+                    return true;
                 }
             }
         }
-        return true;
+        return false;
     }
 
     private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
index e9a6fc8..9b24084 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
@@ -22,6 +22,7 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.data.ApnSetting;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
@@ -118,7 +119,7 @@
      * @return {@code true} if the APN type is metered, {@code false} otherwise.
      */
     public static boolean isMeteredApnType(String type, Phone phone) {
-        if (phone == null) {
+        if (phone == null || TextUtils.isEmpty(type)) {
             return true;
         }
 
@@ -193,11 +194,12 @@
     /**
      * 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) {
+        if (phone == null || apn == null) {
             return true;
         }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index b304e5b..e683c47 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -78,9 +78,12 @@
 import java.io.StringWriter;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -123,17 +126,14 @@
         ApnContext mApnContext;
         int mProfileId;
         int mRilRat;
-        final boolean mUnmeteredUseOnly;
         Message mOnCompletedMsg;
         final int mConnectionGeneration;
 
         ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology,
-                         boolean unmeteredUseOnly,  Message onCompletedMsg,
-                         int connectionGeneration) {
+                         Message onCompletedMsg, int connectionGeneration) {
             mApnContext = apnContext;
             mProfileId = profileId;
             mRilRat = rilRadioTechnology;
-            mUnmeteredUseOnly = unmeteredUseOnly;
             mOnCompletedMsg = onCompletedMsg;
             mConnectionGeneration = connectionGeneration;
         }
@@ -143,7 +143,6 @@
             return "{mTag=" + mTag + " mApnContext=" + mApnContext
                     + " mProfileId=" + mProfileId
                     + " mRat=" + mRilRat
-                    + " mUnmeteredUseOnly=" + mUnmeteredUseOnly
                     + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
         }
     }
@@ -193,7 +192,7 @@
 
     int mTag;
     public int mCid;
-    public HashMap<ApnContext, ConnectionParams> mApnContexts = null;
+    private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>();
     PendingIntent mReconnectIntent = null;
 
 
@@ -222,8 +221,9 @@
     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;
 
-    private static final int CMD_TO_STRING_COUNT = EVENT_RESET - BASE + 1;
+    private static final int CMD_TO_STRING_COUNT = EVENT_REEVALUATE_RESTRICTED_STATE - BASE + 1;
 
     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
     static {
@@ -255,6 +255,8 @@
         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";
     }
     // Convert cmd to string or null if unknown
     static String cmdToString(int cmd) {
@@ -490,8 +492,6 @@
             addState(mDisconnectingState, mDefaultState);
             addState(mDisconnectingErrorCreatingConnection, mDefaultState);
         setInitialState(mInactiveState);
-
-        mApnContexts = new HashMap<>();
     }
 
     /**
@@ -535,7 +535,8 @@
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
 
-        DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId);
+        DataProfile dp = DcTracker.createDataProfile(mApnSetting, cp.mProfileId,
+                mApnSetting.equals(mDct.getPreferredApn()));
 
         // 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
@@ -734,6 +735,8 @@
         mLinkProperties = new LinkProperties();
         mApnContexts.clear();
         mApnSetting = null;
+        mUnmeteredUseOnly = false;
+        mRestrictedNetworkOverride = false;
         mDcFailCause = null;
     }
 
@@ -885,13 +888,18 @@
     }
 
     /**
+     * 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 when this connection was established we had a restricted/privileged
      * NetworkRequest and needed it to overcome data-enabled limitations.
      *
-     * This gets set once per connection setup and is based on conditions at that time.
-     * We could theoretically have dynamic capabilities but now is not a good time to
-     * experiment with that.
-     *
      * 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
@@ -899,38 +907,78 @@
      * 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).
      *
-     * Because we're not supporting dynamic capabilities, if conditions change and we go from
-     * data-enabled to not or vice-versa we will need to tear down networks to deal with it
-     * at connection setup time with the new state.
-     *
      * 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;
 
-    // Should be called once when the call goes active to examine the state of things and
-    // declare the restriction override for the life of the connection
-    private void setNetworkRestriction() {
-        mRestrictedNetworkOverride = false;
-        // first, if we have no restricted requests, this override can stay FALSE:
-        boolean noRestrictedRequests = true;
+    /**
+     * 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()) {
-            noRestrictedRequests &= apnContext.hasNoRestrictedRequests(true /* exclude DUN */);
-        }
-        if (noRestrictedRequests) {
-            return;
+            if (apnContext.hasRestrictedRequests(true /* exclude DUN */)) {
+                isAnyRestrictedRequest = true;
+                break;
+            }
         }
 
-        // Do we need a restricted network to satisfy the request?
-        // Is this network metered?  If not, then don't add restricted
+        // 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;
+            return false;
         }
 
-        // Is data disabled?
-        mRestrictedNetworkOverride = !mDct.isDataEnabled();
+        // 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 (!mDct.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() {
+        // The data connection can only be unmetered used only if all requests' reasons are
+        // unmetered.
+        for (ApnContext apnContext : mApnContexts.keySet()) {
+            DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+            boolean isDataAllowed = mDct.isDataAllowed(apnContext, dataConnectionReasons);
+            if (!isDataAllowed || !dataConnectionReasons.contains(
+                    DataConnectionReasons.DataAllowedReasonType.UNMETERED_APN)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     NetworkCapabilities getNetworkCapabilities() {
@@ -941,8 +989,7 @@
             final String[] types = ApnSetting.getApnTypesStringFromBitmask(
                 mApnSetting.getApnTypeBitmask()).split(",");
             for (String type : types) {
-                if (!mRestrictedNetworkOverride
-                        && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
+                if (!mRestrictedNetworkOverride && mUnmeteredUseOnly
                         && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
                     log("Dropped the metered " + type + " for the unmetered data call.");
                     continue;
@@ -1002,8 +1049,7 @@
             // Mark NOT_METERED in the following cases,
             // 1. All APNs in APN settings are unmetered.
             // 2. The non-restricted data and is intended for unmetered use only.
-            if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
-                    && !mRestrictedNetworkOverride)
+            if ((mUnmeteredUseOnly && !mRestrictedNetworkOverride)
                     || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
                 result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
             } else {
@@ -1012,6 +1058,7 @@
 
             result.maybeMarkCapabilitiesRestricted();
         }
+
         if (mRestrictedNetworkOverride) {
             result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
             // don't use dun on restriction-overriden networks.
@@ -1236,7 +1283,7 @@
                 mAc.disconnected();
                 mAc = null;
             }
-            mApnContexts = null;
+            mApnContexts.clear();
             mReconnectIntent = null;
             mDct = null;
             mApnSetting = null;
@@ -1269,17 +1316,11 @@
                     break;
 
                 case EVENT_DISCONNECT:
-                    if (DBG) {
-                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount="
-                                + mApnContexts.size());
-                    }
-                    deferMessage(msg);
-                    break;
-
                 case EVENT_DISCONNECT_ALL:
+                case EVENT_REEVALUATE_RESTRICTED_STATE:
                     if (DBG) {
-                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount="
-                                + mApnContexts.size());
+                        log("DcDefaultState deferring msg.what=" + getWhatToString(msg.what)
+                                + " RefCount=" + mApnContexts.size());
                     }
                     deferMessage(msg);
                     break;
@@ -1456,8 +1497,10 @@
 
             switch (msg.what) {
                 case EVENT_RESET:
+                case EVENT_REEVALUATE_RESTRICTED_STATE:
                     if (DBG) {
-                        log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
+                        log("DcInactiveState: msg.what=" + getWhatToString(msg.what)
+                                + ", ignore we're already done");
                     }
                     retVal = HANDLED;
                     break;
@@ -1658,8 +1701,13 @@
             }
             misc.subscriberId = mPhone.getSubscriberId();
 
-            setNetworkRestriction();
-            if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride);
+            mRestrictedNetworkOverride = shouldRestrictNetwork();
+            mUnmeteredUseOnly = isUnmeteredUseOnly();
+
+            if (DBG) {
+                log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride
+                        + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly);
+            }
             mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
                     "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
                     50, misc);
@@ -1923,6 +1971,37 @@
                     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());
+                    }
+
+                    // 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());
+                    }
+
+                    retVal = HANDLED;
+                    break;
+                }
                 default:
                     if (VDBG) {
                         log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -2258,7 +2337,6 @@
      * @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 unmeteredUseOnly Indicates the data connection can only used for unmetered purposes
      * @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().
@@ -2266,15 +2344,13 @@
      *                             ignored if obsolete.
      */
     public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
-                        boolean unmeteredUseOnly, Message onCompletedMsg,
-                        int connectionGeneration) {
+                        Message onCompletedMsg, int connectionGeneration) {
         if (DBG) {
-            log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly
-                    + " onCompletedMsg=" + onCompletedMsg);
+            log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
         }
         sendMessage(DataConnection.EVENT_CONNECT,
-                new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly,
-                        onCompletedMsg, connectionGeneration));
+                new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
+                        connectionGeneration));
     }
 
     /**
@@ -2324,6 +2400,15 @@
     }
 
     /**
+     * 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");
+    }
+
+    /**
      * @return The parameters used for initiating a data connection.
      */
     public ConnectionParams getConnectionParams() {
@@ -2367,6 +2452,10 @@
         return (long) response.getSuggestedRetryTime();
     }
 
+    public List<ApnContext> getApnContexts() {
+        return new ArrayList<>(mApnContexts.keySet());
+    }
+
     /**
      * @return the string for msg.what as our info.
      */
@@ -2556,6 +2645,8 @@
         pw.println("mLastFailCause=" + mLastFailCause);
         pw.println("mUserData=" + mUserData);
         pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride));
+        pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride);
+        pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
         pw.println("mInstanceNumber=" + mInstanceNumber);
         pw.println("mAc=" + mAc);
         pw.println("Network capabilities changed history:");
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index 3b856a7..87521b1 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -52,6 +52,10 @@
 
     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;
+
     /**
      * responds to the setInternalDataEnabled call - used internally to turn off data.
      * For example during emergency calls
@@ -69,6 +73,8 @@
      */
     private boolean mCarrierDataEnabled = true;
 
+    private boolean mIsDataEnabled = false;
+
     private Phone mPhone = null;
     private ContentResolver mResolver = null;
 
@@ -88,15 +94,14 @@
     public DataEnabledSettings(Phone phone) {
         mPhone = phone;
         mResolver = mPhone.getContext().getContentResolver();
+        updateDataEnabled();
     }
 
     public synchronized void setInternalDataEnabled(boolean enabled) {
         localLog("InternalDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mInternalDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_INTERNAL_DATA_ENABLED);
-        }
+
+        updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED);
     }
     public synchronized boolean isInternalDataEnabled() {
         return mInternalDataEnabled;
@@ -104,14 +109,11 @@
 
     public synchronized void setUserDataEnabled(boolean enabled) {
         localLog("UserDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
-
         Settings.Global.putInt(mResolver, getMobileDataSettingName(), enabled ? 1 : 0);
 
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_USER_DATA_ENABLED);
-        }
+        updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
     }
+
     public synchronized boolean isUserDataEnabled() {
         boolean defaultVal = "true".equalsIgnoreCase(SystemProperties.get(
                 "ro.com.android.mobiledata", "true"));
@@ -134,33 +136,53 @@
 
     public synchronized void setPolicyDataEnabled(boolean enabled) {
         localLog("PolicyDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mPolicyDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_POLICY_DATA_ENABLED);
-        }
+
+        updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED);
     }
+
     public synchronized boolean isPolicyDataEnabled() {
         return mPolicyDataEnabled;
     }
 
     public synchronized void setCarrierDataEnabled(boolean enabled) {
         localLog("CarrierDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mCarrierDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_DATA_ENABLED_BY_CARRIER);
-        }
+
+        updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER);
     }
+
     public synchronized boolean isCarrierDataEnabled() {
         return mCarrierDataEnabled;
     }
 
+    public synchronized void updateProvisionedChanged() {
+        updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED);
+    }
+
+    public synchronized void updateProvisioningDataEnabled() {
+        updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED);
+    }
+
     public synchronized boolean isDataEnabled() {
+        return mIsDataEnabled;
+    }
+
+    private synchronized void updateDataEnabledAndNotify(int reason) {
+        boolean prevDataEnabled = mIsDataEnabled;
+
+        updateDataEnabled();
+
+        if (prevDataEnabled != mIsDataEnabled) {
+            notifyDataEnabledChanged(!prevDataEnabled, reason);
+        }
+    }
+
+    private synchronized void updateDataEnabled() {
         if (isProvisioning()) {
-            return isProvisioningDataEnabled();
+            mIsDataEnabled = isProvisioningDataEnabled();
         } else {
-            return mInternalDataEnabled && isUserDataEnabled()
+            mIsDataEnabled = mInternalDataEnabled && isUserDataEnabled()
                     && mPolicyDataEnabled && mCarrierDataEnabled;
         }
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index a5ae762..322b9c0 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -43,6 +43,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * Data Connection Controller which is a package visible class and controls
@@ -314,7 +315,8 @@
                     continue;
                 }
 
-                if (dc.mApnContexts.size() == 0) {
+                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
@@ -325,7 +327,7 @@
                     }
                     if (newState.getActive() == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
                         if (mDct.isCleanupRequired.get()) {
-                            apnsToCleanup.addAll(dc.mApnContexts.keySet());
+                            apnsToCleanup.addAll(apnContexts);
                             mDct.isCleanupRequired.set(false);
                         } else {
                             DcFailCause failCause = DcFailCause.fromInt(newState.getStatus());
@@ -341,7 +343,7 @@
                                     log("onDataStateChanged: inactive, add to cleanup list. "
                                             + "failCause=" + failCause);
                                 }
-                                apnsToCleanup.addAll(dc.mApnContexts.keySet());
+                                apnsToCleanup.addAll(apnContexts);
                             } else {
                                 if (DBG) {
                                     log("onDataStateChanged: inactive, add to retry list. "
@@ -382,16 +384,16 @@
                                     }
                                     if (needToClean) {
                                         if (DBG) {
-                                            log("onDataStateChanged: addr change," +
-                                                    " cleanup apns=" + dc.mApnContexts +
-                                                    " oldLp=" + result.oldLp +
-                                                    " newLp=" + result.newLp);
+                                            log("onDataStateChanged: addr change,"
+                                                    + " cleanup apns=" + apnContexts
+                                                    + " oldLp=" + result.oldLp
+                                                    + " newLp=" + result.newLp);
                                         }
-                                        apnsToCleanup.addAll(dc.mApnContexts.keySet());
+                                        apnsToCleanup.addAll(apnContexts);
                                     } else {
                                         if (DBG) log("onDataStateChanged: simple change");
 
-                                        for (ApnContext apnContext : dc.mApnContexts.keySet()) {
+                                        for (ApnContext apnContext : apnContexts) {
                                              mPhone.notifyDataConnection(
                                                  PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
                                                  apnContext.getApnType());
@@ -403,10 +405,10 @@
                                     }
                                 }
                             } else {
-                                apnsToCleanup.addAll(dc.mApnContexts.keySet());
+                                apnsToCleanup.addAll(apnContexts);
                                 if (DBG) {
                                     log("onDataStateChanged: interface change, cleanup apns="
-                                            + dc.mApnContexts);
+                                            + apnContexts);
                                 }
                             }
                         }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
old mode 100644
new mode 100755
index 5faf169..24d77ca
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -18,6 +18,9 @@
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 
+import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
+import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
+
 import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -76,7 +79,6 @@
 import android.util.SparseArray;
 import android.view.WindowManager;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CarrierActionAgent;
 import com.android.internal.telephony.DctConstants;
@@ -102,6 +104,7 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.PriorityQueue;
 import java.util.Set;
@@ -326,7 +329,7 @@
                 DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
-                DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
+                DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE);
     }
 
     /**
@@ -536,19 +539,7 @@
 
     private int mDisconnectPendingCount = 0;
 
-    /** Indicate if metered APNs are disabled.
-     *  set to block all the metered APNs from continuously sending requests, which causes
-     *  undesired network load */
-    private boolean mMeteredApnDisabled = false;
-
-    /**
-     * int to remember whether has setDataProfiles and with roaming or not.
-     * 0: default, has never set data profile
-     * 1: has set data profile with home protocol
-     * 2: has set data profile with roaming protocol
-     * This is not needed once RIL command is updated to support both home and roaming protocol.
-     */
-    private int mSetDataProfileStatus = 0;
+    private ArrayList<DataProfile> mLastDataProfileList = new ArrayList<>();
 
     /**
      * Handles changes to the APN db.
@@ -828,51 +819,23 @@
      *
      * 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 tear down any metered apn type that was enabled anyway by a privileged request.
-     * This allows us to reconnect to it in an unrestricted way.
+     * 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() {
-        if (mDataEnabledSettings.isDataEnabled()) {
-            for (ApnContext apnContext : mApnContexts.values()) {
-                if (apnContext.isConnectedOrConnecting()) {
-                    final DataConnection dataConnection = apnContext.getDataConnection();
-                    if (dataConnection != null) {
-                        final NetworkCapabilities netCaps = dataConnection.getNetworkCapabilities();
-                        if (netCaps != null && !netCaps.hasCapability(NetworkCapabilities
-                                .NET_CAPABILITY_NOT_RESTRICTED) && !netCaps.hasCapability(
-                                NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
-                            if (DBG) {
-                                log("Tearing down restricted metered net:" + apnContext);
-                            }
-                            // Tearing down the restricted metered data call when
-                            // conditions change. This will allow reestablishing a new unrestricted
-                            // data connection.
-                            apnContext.setReason(Phone.REASON_DATA_ENABLED);
-                            cleanUpConnection(true, apnContext);
-                        } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone)
-                                && (netCaps != null && netCaps.hasCapability(
-                                        NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
-                            if (DBG) {
-                                log("Tearing down unmetered net:" + apnContext);
-                            }
-                            // The APN settings is metered, but the data was still marked as
-                            // unmetered data, must be the unmetered data connection brought up when
-                            // data is off. We need to tear that down when data is enabled again.
-                            // This will allow reestablishing a new full capability data connection.
-                            apnContext.setReason(Phone.REASON_DATA_ENABLED);
-                            cleanUpConnection(true, apnContext);
-                        }
-                    }
-                }
-            }
+        for (DataConnection dataConnection : mDataConnections.values()) {
+            dataConnection.reevaluateRestrictedState();
         }
     }
 
     private void onDeviceProvisionedChange() {
+        mDataEnabledSettings.updateProvisionedChanged();
+        // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+        // handle the rest from there.
         if (isDataEnabled()) {
             reevaluateDataConnections();
             onTrySetupData(Phone.REASON_DATA_ENABLED);
@@ -881,6 +844,17 @@
         }
     }
 
+    private void onDeviceProvisioningDataChange() {
+        mDataEnabledSettings.updateProvisioningDataEnabled();
+        // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+        // handle the rest from there.
+        if (isDataEnabled()) {
+            reevaluateDataConnections();
+            onTrySetupData(Phone.REASON_DATA_ENABLED);
+        } else {
+            onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
+        }
+    }
 
     public long getSubId() {
         return mPhone.getSubId();
@@ -1240,7 +1214,8 @@
      *                              provided.
      * @return True if data connection is allowed, otherwise false.
      */
-    boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) {
+    public boolean isDataAllowed(ApnContext apnContext,
+                                 DataConnectionReasons dataConnectionReasons) {
         // Step 1: Get all environment conditions.
         // Step 2: Special handling for emergency APN.
         // Step 3. Build disallowed reasons.
@@ -1363,11 +1338,11 @@
             reasons.add(DataAllowedReasonType.UNMETERED_APN);
         }
 
-        // If the request is restricted and there are only disallowed reasons due to data
-        // disabled, we should allow the data.
+        // 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.
         if (apnContext != null
-                && !apnContext.hasNoRestrictedRequests(true)
-                && reasons.contains(DataDisallowedReasonType.DATA_DISABLED)) {
+                && apnContext.hasRestrictedRequests(true)
+                && !reasons.allowed()) {
             reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
         }
 
@@ -1487,8 +1462,7 @@
                 }
             }
 
-            boolean retValue = setupData(apnContext, radioTech, dataConnectionReasons.contains(
-                    DataAllowedReasonType.UNMETERED_APN));
+            boolean retValue = setupData(apnContext, radioTech);
             notifyOffApnsOfAvailability(apnContext.getReason());
 
             if (DBG) log("trySetupData: X retValue=" + retValue);
@@ -1732,29 +1706,13 @@
         ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
 
         // Places to look for tether APN in order: TETHER_DUN_APN setting (to be deprecated soon),
-        // APN database, and config_tether_apndata resource (to be deprecated soon).
+        // APN database
         String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
         if (!TextUtils.isEmpty(apnData)) {
             dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
             if (VDBG) log("fetchDunApns: dunCandidates from Setting: " + dunCandidates);
         }
 
-        // todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
-        // If TETHER_DUN_APN isn't set or APN database doesn't have dun APN,
-        // try the resource as last resort.
-        if (dunCandidates.isEmpty()) {
-            String[] apnArrayData = mPhone.getContext().getResources()
-                .getStringArray(R.array.config_tether_apndata);
-            if (!ArrayUtils.isEmpty(apnArrayData)) {
-                for (String apnString : apnArrayData) {
-                    ApnSetting apn = ApnSetting.fromString(apnString);
-                    // apn may be null if apnString isn't valid or has error parsing
-                    if (apn != null) dunCandidates.add(apn);
-                }
-                if (VDBG) log("fetchDunApns: dunCandidates from resource: " + dunCandidates);
-            }
-        }
-
         if (dunCandidates.isEmpty()) {
             if (!ArrayUtils.isEmpty(mAllApnSettings)) {
                 for (ApnSetting apn : mAllApnSettings) {
@@ -1792,13 +1750,24 @@
                 .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_SET_SET;
+        }
+
+        int setId;
         if (c.getCount() < 1) {
             loge("getPreferredApnSetId: no APNs found");
-            return Telephony.Carriers.NO_SET_SET;
+            setId = Telephony.Carriers.NO_SET_SET;
         } else {
             c.moveToFirst();
-            return c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
+            setId = c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
         }
+
+        if (!c.isClosed()) {
+            c.close();
+        }
+        return setId;
     }
 
     public boolean hasMatchedTetherApnSetting() {
@@ -1902,11 +1871,9 @@
      *
      * @param apnContext APN context
      * @param radioTech RAT of the data connection
-     * @param unmeteredUseOnly True if this data connection should be only used for unmetered
-     *                         purposes only.
      * @return True if successful, otherwise false.
      */
-    private boolean setupData(ApnContext apnContext, int radioTech, boolean unmeteredUseOnly) {
+    private boolean setupData(ApnContext apnContext, int radioTech) {
         if (DBG) log("setupData: apnContext=" + apnContext);
         apnContext.requestLog("setupData");
         ApnSetting apnSetting;
@@ -1919,9 +1886,13 @@
             return false;
         }
 
-        int profileId = apnSetting.getProfileId();
-        if (profileId == 0) {
-            profileId = getApnProfileID(apnContext.getApnType());
+        // 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
@@ -1992,7 +1963,7 @@
         Message msg = obtainMessage();
         msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
         msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
-        dataConnection.bringUp(apnContext, profileId, radioTech, unmeteredUseOnly, msg, generation);
+        dataConnection.bringUp(apnContext, profileId, radioTech, msg, generation);
 
         if (DBG) log("setupData: initing!");
         return true;
@@ -2053,7 +2024,8 @@
         } else {
             if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
 
-            mDataServiceManager.setInitialAttachApn(createDataProfile(initialAttachApnSetting),
+            mDataServiceManager.setInitialAttachApn(createDataProfile(initialAttachApnSetting,
+                            initialAttachApnSetting.equals(getPreferredApn())),
                     mPhone.getServiceState().getDataRoamingFromRegistration(), null);
         }
     }
@@ -2122,7 +2094,7 @@
         CarrierConfigManager configManager = (CarrierConfigManager)
                 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager != null) {
-            PersistableBundle bundle = configManager.getConfig();
+            PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
             if (bundle != null) {
                 singleDcRats = bundle.getIntArray(
                         CarrierConfigManager.KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY);
@@ -2152,7 +2124,7 @@
     private void restartRadio() {
         if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
         cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
-        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
+        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
@@ -2232,7 +2204,7 @@
         createAllApnList();
         setDataProfilesAsNeeded();
         setInitialAttachApn();
-        if (mPhone.mCi.getRadioState().isOn()) {
+        if (mPhone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
             if (DBG) log("onRecordsLoadedOrSubIdChanged: notifying data availability");
             notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
         }
@@ -2644,6 +2616,8 @@
     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
@@ -2679,6 +2653,13 @@
         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");
 
             setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
@@ -3218,17 +3199,22 @@
     private void setDataProfilesAsNeeded() {
         if (DBG) log("setDataProfilesAsNeeded");
 
-        ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
+        ArrayList<DataProfile> dataProfileList = new ArrayList<>();
+
         for (ApnSetting apn : mAllApnSettings) {
-            if (apn.getModemCognitive()) {
-                DataProfile dp = createDataProfile(apn);
-                if (!dps.contains(dp)) {
-                    dps.add(dp);
-                }
+            DataProfile dp = createDataProfile(apn, apn.equals(getPreferredApn()));
+            if (!dataProfileList.contains(dp)) {
+                dataProfileList.add(dp);
             }
         }
-        if (dps.size() > 0) {
-            mDataServiceManager.setDataProfile(dps,
+
+        // 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);
         }
     }
@@ -3339,9 +3325,9 @@
             dest.getApnName(), proxy, port, mmsc, mmsProxy, mmsPort, dest.getUser(),
             dest.getPassword(), dest.getAuthType(), resultApnType, protocol, roamingProtocol,
             dest.isEnabled(), networkTypeBitmask, dest.getProfileId(),
-            (dest.getModemCognitive() || src.getModemCognitive()), dest.getMaxConns(),
+            (dest.isPersistent() || src.isPersistent()), dest.getMaxConns(),
             dest.getWaitTime(), dest.getMaxConnsTime(), dest.getMtu(), dest.getMvnoType(),
-            dest.getMvnoMatchData(), dest.getApnSetId());
+            dest.getMvnoMatchData(), dest.getApnSetId(), dest.getCarrierId());
     }
 
     private DataConnection createDataConnection() {
@@ -3527,7 +3513,7 @@
         }
     }
 
-    private ApnSetting getPreferredApn() {
+    ApnSetting getPreferredApn() {
         if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
             log("getPreferredApn: mAllApnSettings is empty");
             return null;
@@ -3717,10 +3703,15 @@
                 onDeviceProvisionedChange();
                 break;
 
+            case DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE:
+                onDeviceProvisioningDataChange();
+                break;
+
             case DctConstants.EVENT_REDIRECTION_DETECTED:
                 String url = (String) msg.obj;
                 log("dataConnectionTracker.handleMessage: EVENT_REDIRECTION_DETECTED=" + url);
                 onDataConnectionRedirected(url);
+                break;
 
             case DctConstants.EVENT_RADIO_AVAILABLE:
                 onRadioAvailable();
@@ -3982,10 +3973,6 @@
         cleanUpAllConnections(cause, null);
     }
 
-    public void updateRecords() {
-        onUpdateIcc();
-    }
-
     public void cleanUpAllConnections(String cause, Message disconnectAllCompleteMsg) {
         log("cleanUpAllConnections");
         if (disconnectAllCompleteMsg != null) {
@@ -4012,8 +3999,8 @@
         mAllDataDisconnectedRegistrants.notifyRegistrants();
     }
 
-    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
-        mAllDataDisconnectedRegistrants.addUnique(h, what, obj);
+    public void registerForAllDataDisconnected(Handler h, int what) {
+        mAllDataDisconnectedRegistrants.addUnique(h, what, null);
 
         if (isDisconnected()) {
             log("notify All Data Disconnected");
@@ -4444,7 +4431,7 @@
                 }
                 // check if this dc is still connecting
                 if (cid == -1) {
-                    for (ApnContext apnContext : dc.mApnContexts.keySet()) {
+                    for (ApnContext apnContext : dc.getApnContexts()) {
                         if (apnContext.getState() == DctConstants.State.CONNECTING) {
                             if (VDBG) log("  found potential " + dc);
                             dcList.add(dc);
@@ -4459,11 +4446,12 @@
             return;
         }
         for (DataConnection dc : dcList) {
-            if (dc.mApnContexts.size() == 0) {
+            List<ApnContext> apnContextList = dc.getApnContexts();
+            if (apnContextList.size() == 0) {
                 break;
             }
             // send one out for each apn type in play
-            for (ApnContext apnContext : dc.mApnContexts.keySet()) {
+            for (ApnContext apnContext : apnContextList) {
                 String apnType = apnContext.getApnType();
 
                 final Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
@@ -4755,16 +4743,16 @@
         }
     }
 
-    private static DataProfile createDataProfile(ApnSetting apn) {
-        return createDataProfile(apn, apn.getProfileId());
+    private static DataProfile createDataProfile(ApnSetting apn, boolean isPreferred) {
+        return createDataProfile(apn, apn.getProfileId(), isPreferred);
     }
 
     @VisibleForTesting
-    public static DataProfile createDataProfile(ApnSetting apn, int profileId) {
+    public static DataProfile createDataProfile(ApnSetting apn, int profileId,
+                                                boolean isPreferred) {
         int profileType;
 
-        int bearerBitmap = 0;
-        bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
+        int bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
                 apn.getNetworkTypeBitmask());
 
         if (bearerBitmap == 0) {
@@ -4776,12 +4764,11 @@
         }
 
         return new DataProfile(profileId, apn.getApnName(),
-            ApnSetting.getProtocolStringFromInt(apn.getProtocol()), apn.getAuthType(),
-            apn.getUser(), apn.getPassword(), profileType, apn.getMaxConnsTime(),
-            apn.getMaxConns(), apn.getWaitTime(), apn.isEnabled(), apn.getApnTypeBitmask(),
-            ApnSetting.getProtocolStringFromInt(apn.getRoamingProtocol()), bearerBitmap,
-            apn.getMtu(), ApnSetting.getMvnoTypeStringFromInt(apn.getMvnoType()),
-            apn.getMvnoMatchData(), apn.getModemCognitive());
+                ApnSetting.getProtocolStringFromInt(apn.getProtocol()), apn.getAuthType(),
+                apn.getUser(), apn.getPassword(), profileType, apn.getMaxConnsTime(),
+                apn.getMaxConns(),  apn.getWaitTime(), apn.isEnabled(), apn.getApnTypeBitmask(),
+                ApnSetting.getProtocolStringFromInt(apn.getRoamingProtocol()), bearerBitmap,
+                apn.getMtu(), apn.isPersistent(), isPreferred);
     }
 
     private void onDataServiceBindingChanged(boolean bound) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index 339fa9a..5c44f37 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -18,7 +18,6 @@
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import android.content.Context;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
@@ -26,9 +25,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.Rlog;
 import android.util.LocalLog;
 
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.SubscriptionMonitor;
@@ -52,7 +53,7 @@
     private final HashMap<NetworkRequest, LocalLog> mSpecificRequests =
             new HashMap<NetworkRequest, LocalLog>();
 
-    private final int mPhoneId;
+    private final Phone mPhone;
     // Only when this network factory is active, it will apply any network requests.
     private boolean mIsActive;
     // Whether this network factory is active and should handle default network requests.
@@ -68,28 +69,31 @@
     private static final int EVENT_NETWORK_REQUEST              = 3;
     private static final int EVENT_NETWORK_RELEASE              = 4;
 
-    public TelephonyNetworkFactory(PhoneSwitcher phoneSwitcher,
-            SubscriptionController subscriptionController, SubscriptionMonitor subscriptionMonitor,
-            Looper looper, Context context, int phoneId, DcTracker dcTracker) {
-        super(looper, context, "TelephonyNetworkFactory[" + phoneId + "]", null);
+    public TelephonyNetworkFactory(SubscriptionMonitor subscriptionMonitor, Looper looper,
+                                   Phone phone) {
+        super(looper, phone.getContext(), "TelephonyNetworkFactory[" + phone.getPhoneId()
+                + "]", null);
+        mPhone = phone;
         mInternalHandler = new InternalHandler(looper);
 
-        setCapabilityFilter(makeNetworkFilter(subscriptionController, phoneId));
+        mSubscriptionController = SubscriptionController.getInstance();
+
+        setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId()));
         setScoreFilter(TELEPHONY_NETWORK_SCORE);
 
-        mPhoneSwitcher = phoneSwitcher;
-        mSubscriptionController = subscriptionController;
+        mPhoneSwitcher = PhoneSwitcher.getInstance();
         mSubscriptionMonitor = subscriptionMonitor;
-        mPhoneId = phoneId;
-        LOG_TAG = "TelephonyNetworkFactory[" + phoneId + "]";
-        mDcTracker = dcTracker;
+        LOG_TAG = "TelephonyNetworkFactory[" + mPhone.getPhoneId() + "]";
+        // TODO: Will need to dynamically route network requests to the corresponding DcTracker in
+        // the future. For now we route everything to WWAN.
+        mDcTracker = mPhone.getDcTracker(TransportType.WWAN);
 
         mIsActive = false;
-        mPhoneSwitcher.registerForActivePhoneSwitch(mPhoneId, mInternalHandler,
-                EVENT_ACTIVE_PHONE_SWITCH, null);
+        mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH,
+                null);
 
         mSubscriptionId = INVALID_SUBSCRIPTION_ID;
-        mSubscriptionMonitor.registerForSubscriptionChanged(mPhoneId, mInternalHandler,
+        mSubscriptionMonitor.registerForSubscriptionChanged(mPhone.getPhoneId(), mInternalHandler,
                 EVENT_SUBSCRIPTION_CHANGED, null);
 
         mIsActiveForDefault = false;
@@ -183,11 +187,13 @@
 
     // apply or revoke requests if our active-ness changes
     private void onActivePhoneSwitch() {
-        final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId);
-        final boolean newIsActiveForDefault = mPhoneSwitcher.isActiveForDefaultRequests(mPhoneId);
+        final boolean newIsActive = mPhoneSwitcher.shouldApplySpecifiedRequests(
+                mPhone.getPhoneId());
+        final boolean newIsActiveForDefault =
+                mPhoneSwitcher.shouldApplyUnspecifiedRequests(mPhone.getPhoneId());
 
         String logString = "onActivePhoneSwitch(newIsActive " + newIsActive + ", "
-                + "newIsActive " + newIsActiveForDefault + ")";
+                + "newIsActiveForDefault " + newIsActiveForDefault + ")";
         if (DBG) log(logString);
 
         applyRequests(mSpecificRequests, getAction(mIsActive, newIsActive), logString);
@@ -201,7 +207,8 @@
     // watch for phone->subId changes, reapply new filter and let
     // that flow through to apply/revoke of requests
     private void onSubIdChange() {
-        final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId(mPhoneId);
+        final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId(
+                mPhone.getPhoneId());
         if (mSubscriptionId != newSubscriptionId) {
             if (DBG) log("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId);
             mSubscriptionId = newSubscriptionId;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index 5c5d244..6de3984 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -194,7 +194,16 @@
     @Override
     public void getAllProfiles(String callingPackage, String cardId,
             IGetAllProfilesCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -234,7 +243,16 @@
     @Override
     public void getProfile(String callingPackage, String cardId, String iccid,
             IGetProfileCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -273,7 +291,16 @@
     @Override
     public void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
             IDisableProfileCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -312,7 +339,16 @@
     @Override
     public void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
             ISwitchToProfileCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -369,7 +405,16 @@
     @Override
     public void setNickname(String callingPackage, String cardId, String iccid, String nickname,
             ISetNicknameCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -408,7 +453,16 @@
     @Override
     public void deleteProfile(String callingPackage, String cardId, String iccid,
             IDeleteProfileCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -449,7 +503,16 @@
     @Override
     public void resetMemory(String callingPackage, String cardId,
             @EuiccCardManager.ResetOption int options, IResetMemoryCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -490,7 +553,16 @@
     @Override
     public void getDefaultSmdpAddress(String callingPackage, String cardId,
             IGetDefaultSmdpAddressCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -529,7 +601,16 @@
     @Override
     public void getSmdsAddress(String callingPackage, String cardId,
             IGetSmdsAddressCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -568,7 +649,16 @@
     @Override
     public void setDefaultSmdpAddress(String callingPackage, String cardId, String address,
             ISetDefaultSmdpAddressCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -607,7 +697,16 @@
     @Override
     public void getRulesAuthTable(String callingPackage, String cardId,
             IGetRulesAuthTableCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -647,7 +746,16 @@
     @Override
     public void getEuiccChallenge(String callingPackage, String cardId,
             IGetEuiccChallengeCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -686,7 +794,16 @@
     @Override
     public void getEuiccInfo1(String callingPackage, String cardId,
             IGetEuiccInfo1Callback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -725,7 +842,16 @@
     @Override
     public void getEuiccInfo2(String callingPackage, String cardId,
             IGetEuiccInfo2Callback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -765,7 +891,16 @@
     public void authenticateServer(String callingPackage, String cardId, String matchingId,
             byte[] serverSigned1, byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed,
             byte[] serverCertificate, IAuthenticateServerCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -806,7 +941,16 @@
     public void prepareDownload(String callingPackage, String cardId, @Nullable byte[] hashCc,
             byte[] smdpSigned2, byte[] smdpSignature2, byte[] smdpCertificate,
             IPrepareDownloadCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -846,7 +990,16 @@
     @Override
     public void loadBoundProfilePackage(String callingPackage, String cardId,
             byte[] boundProfilePackage, ILoadBoundProfilePackageCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -887,7 +1040,16 @@
     @Override
     public void cancelSession(String callingPackage, String cardId, byte[] transactionId,
             @EuiccCardManager.CancelReason int reason, ICancelSessionCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -926,7 +1088,16 @@
     @Override
     public void listNotifications(String callingPackage, String cardId,
             @EuiccNotification.Event int events, IListNotificationsCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -966,7 +1137,16 @@
     @Override
     public void retrieveNotificationList(String callingPackage, String cardId,
             @EuiccNotification.Event int events, IRetrieveNotificationListCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -1006,7 +1186,16 @@
     @Override
     public void retrieveNotification(String callingPackage, String cardId, int seqNumber,
             IRetrieveNotificationCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED, null);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
@@ -1046,7 +1235,16 @@
     @Override
     public void removeNotificationFromList(String callingPackage, String cardId, int seqNumber,
             IRemoveNotificationFromListCallback callback) {
-        checkCallingPackage(callingPackage);
+        try {
+            checkCallingPackage(callingPackage);
+        } catch (SecurityException se) {
+            try {
+                callback.onComplete(EuiccCardManager.RESULT_CALLER_NOT_ALLOWED);
+            } catch (RemoteException re) {
+                loge("callback onComplete failure after checkCallingPackage.", re);
+            }
+            return;
+        }
 
         EuiccCard card = getEuiccCard(cardId);
         if (card == null) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index f6efc5f..c3ff090 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -29,8 +29,6 @@
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.VisualVoicemailSmsFilter;
-import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.uicc.UsimServiceTable;
 
 /**
@@ -154,15 +152,6 @@
         }
         // update voice mail count in Phone
         mPhone.setVoiceMessageCount(voicemailCount);
-        // store voice mail count in SIM & shared preferences
-        IccRecords records = UiccController.getInstance().getIccRecords(
-                mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
-        if (records != null) {
-            log("updateMessageWaitingIndicator: updating SIM Records");
-            records.setVoiceMessageWaiting(1, voicemailCount);
-        } else {
-            log("updateMessageWaitingIndicator: SIM Records not found");
-        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index dc8c126..ea5f671 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1620,6 +1620,11 @@
     }
 
     @Override
+    public boolean isImsCapabilityAvailable(int capability, int regTech) {
+        return mCT.isImsCapabilityAvailable(capability, regTech);
+    }
+
+    @Override
     public boolean isVolteEnabled() {
         return mCT.isVolteEnabled();
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 714343d..c08ce6c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -479,7 +479,7 @@
     }
 
     @Override
-    public boolean isDataAllowed() {
+    public boolean isDataAllowed(int apnType) {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 82f4702..8fea1aa 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -46,6 +46,7 @@
 import android.telecom.ConferenceParticipant;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
@@ -55,12 +56,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -302,6 +304,8 @@
         HOLDING_TO_ANSWER_INCOMING,
         // Pending resuming the foreground call after some kind of failure
         PENDING_RESUME_FOREGROUND_AFTER_FAILURE,
+        // Pending holding a call to dial another outgoing call
+        HOLDING_TO_DIAL_OUTGOING,
     }
 
     //***** Instance Variables
@@ -484,6 +488,8 @@
                 PreciseDisconnectCause.SIP_NOT_REACHABLE);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR,
                 PreciseDisconnectCause.SIP_CLIENT_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+                PreciseDisconnectCause.SIP_TRANSACTION_DOES_NOT_EXIST);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR,
                 PreciseDisconnectCause.SIP_SERVER_INTERNAL_ERROR);
         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE,
@@ -647,12 +653,12 @@
     private boolean mNotifyHandoverVideoFromLTEToWifi = false;
 
     /**
-     * When {@code} false, indicates that no handover from LTE to WIFI has occurred during the start
-     * of the call.
+     * When {@code} false, indicates that no handover from LTE to WIFI has been attempted during the
+     * start of the call.
      * When {@code true}, indicates that the start of call handover from LTE to WIFI has been
-     * attempted (it may have suceeded or failed).
+     * attempted (it may have succeeded or failed).
      */
-    private boolean mHasPerformedStartOfCallHandover = false;
+    private boolean mHasAttemptedStartOfCallHandover = false;
 
     /**
      * Carrier configuration option which determines whether the carrier supports the
@@ -832,7 +838,7 @@
         // Only close on valid session.
         if (mImsManager != null) {
             try {
-                mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback);
+                mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback.getBinder());
             } catch (ImsException e) {
                 Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback.");
             }
@@ -966,7 +972,7 @@
             // Cache the video state for pending MO call.
             mPendingCallVideoState = videoState;
             mPendingIntentExtras = dialArgs.intentExtras;
-            holdActiveCall();
+            holdActiveCallForPendingMo();
         }
 
         ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
@@ -1147,6 +1153,9 @@
                         fromCode = Integer.parseInt(values[0]);
                     }
                     String message = values[1];
+                    if (message == null) {
+                        message = "";
+                    }
                     int toCode = Integer.parseInt(values[2]);
 
                     addReasonCodeRemapping(fromCode, message, toCode);
@@ -1334,6 +1343,28 @@
         }
     }
 
+    private void holdActiveCallForPendingMo() throws CallStateException {
+        if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD
+                || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
+            logi("Ignoring hold request while already holding or swapping");
+            return;
+        }
+        ImsCall callToHold = mForegroundCall.getImsCall();
+
+        mHoldSwitchingState = HoldSwapState.HOLDING_TO_DIAL_OUTGOING;
+        logHoldSwapState("holdActiveCallForPendingMo");
+
+        mForegroundCall.switchWith(mBackgroundCall);
+        try {
+            callToHold.hold();
+            mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(),
+                    ImsCommand.IMS_CMD_HOLD);
+        } catch (ImsException e) {
+            mForegroundCall.switchWith(mBackgroundCall);
+            throw new CallStateException(e.getMessage());
+        }
+    }
+
     /**
      * Holds the active call, possibly resuming the already-held background call if it exists.
      */
@@ -1835,6 +1866,7 @@
                     ImsCallProfile.DIALSTRING_USSD);
 
             mUssdSession = mImsManager.makeCall(profile, callees, mImsUssdListener);
+            mPendingUssd = response;
         } catch (ImsException e) {
             loge("sendUSSD : " + e);
             mPhone.sendErrorResponse(response, e);
@@ -1845,11 +1877,7 @@
     public void cancelUSSD() {
         if (mUssdSession == null) return;
 
-        try {
-            mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
-        } catch (ImsException e) {
-        }
-
+        mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
     }
 
     private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
@@ -1981,22 +2009,29 @@
     @VisibleForTesting
     public int maybeRemapReasonCode(ImsReasonInfo reasonInfo) {
         int code = reasonInfo.getCode();
-
-        Pair<Integer, String> toCheck = new Pair<>(code, reasonInfo.getExtraMessage());
-        Pair<Integer, String> wildcardToCheck = new Pair<>(null, reasonInfo.getExtraMessage());
+        String reason = reasonInfo.getExtraMessage();
+        if (reason == null) {
+            reason = "";
+        }
+        log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
+                + reason);
+        Pair<Integer, String> toCheck = new Pair<>(code, reason);
+        Pair<Integer, String> wildcardToCheck = new Pair<>(null, reason);
         if (mImsReasonCodeMap.containsKey(toCheck)) {
             int toCode = mImsReasonCodeMap.get(toCheck);
 
             log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
-                    + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
+                    + reason + " ; toCode = " + toCode);
             return toCode;
-        } else if (mImsReasonCodeMap.containsKey(wildcardToCheck)) {
+        } else if (!reason.isEmpty() && mImsReasonCodeMap.containsKey(wildcardToCheck)) {
             // Handle the case where a wildcard is specified for the fromCode; in this case we will
             // match without caring about the fromCode.
+            // If the reason is empty, we won't do wildcard remapping; otherwise we'd basically be
+            // able to remap all ImsReasonInfo codes to a single code, which is not desirable.
             int toCode = mImsReasonCodeMap.get(wildcardToCheck);
 
             log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() +
-                    " ; message = " + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
+                    " ; message = " + reason + " ; toCode = " + toCode);
             return toCode;
         }
         return code;
@@ -2230,13 +2265,15 @@
                     // Schedule check to see if handover succeeded.
                     sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
                             HANDOVER_TO_WIFI_TIMEOUT_MS);
+                    mHasAttemptedStartOfCallHandover = false;
                 } else {
                     // No wifi connectivity, so keep track of network availability for potential
                     // handover.
                     registerForConnectivityChanges();
+                    // No WIFI, so assume we've already attempted a handover.
+                    mHasAttemptedStartOfCallHandover = true;
                 }
             }
-            mHasPerformedStartOfCallHandover = false;
             mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession());
         }
 
@@ -2390,9 +2427,6 @@
                 if (mRingingCall.getState().isRinging()) {
                     // Drop pending MO. We should address incoming call first
                     mPendingMO = null;
-                } else if (mPendingMO != null
-                        && mPendingMO.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
-                    sendEmptyMessage(EVENT_DIAL_PENDINGMO);
                 }
             }
 
@@ -2437,6 +2471,19 @@
                     logHoldSwapState("onCallTerminated hold to answer case");
                     sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
                 }
+            } else if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) {
+                // The call that we were gonna hold might've gotten terminated. If that's the case,
+                // dial mPendingMo if present.
+                if (mPendingMO == null
+                        || mPendingMO.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
+                    mHoldSwitchingState = HoldSwapState.INACTIVE;
+                    logHoldSwapState("onCallTerminated hold to dial but no pendingMo");
+                }
+                if (imsCall != mPendingMO.getImsCall()) {
+                    sendEmptyMessage(EVENT_DIAL_PENDINGMO);
+                    mHoldSwitchingState = HoldSwapState.INACTIVE;
+                    logHoldSwapState("onCallTerminated hold to dial, dial pendingMo");
+                }
             }
 
             if (mShouldUpdateImsConfigOnDisconnect) {
@@ -2477,14 +2524,12 @@
                     } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING
                             && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
                         sendEmptyMessage(EVENT_ANSWER_WAITING_CALL);
+                    } else if (mPendingMO != null
+                            && mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) {
+                        dialPendingMO();
+                        mHoldSwitchingState = HoldSwapState.INACTIVE;
+                        logHoldSwapState("onCallHeld hold to dial");
                     } else {
-                        //when multiple connections belong to background call,
-                        //only the first callback reaches here
-                        //otherwise the oldState is already HOLDING
-                        if (mPendingMO != null) {
-                            dialPendingMO();
-                        }
-
                         // In this case there will be no call resumed, so we can assume that we
                         // are done switching fg and bg calls now.
                         // This may happen if there is no BG call and we are holding a call so that
@@ -2520,6 +2565,16 @@
                     if (mPendingMO != null) {
                         dialPendingMO();
                     }
+                    mHoldSwitchingState = HoldSwapState.INACTIVE;
+                } else if (mPendingMO.isEmergency()) {
+                    // If mPendingMO is an emergency call, disconnect the call that we tried to
+                    // hold.
+                    mBackgroundCall.getImsCall().terminate(ImsReasonInfo.CODE_UNSPECIFIED);
+                    if (imsCall != mCallExpectedToResume) {
+                        mCallExpectedToResume = null;
+                    }
+                    // Leave mHoldSwitchingState as is for now -- we'll reset it
+                    // in onCallTerminated, which will also dial the outgoing emergency call.
                 } else if (bgState == ImsPhoneCall.State.ACTIVE) {
                     mForegroundCall.switchWith(mBackgroundCall);
 
@@ -2530,6 +2585,7 @@
                     if (imsCall != mCallExpectedToResume) {
                         mCallExpectedToResume = null;
                     }
+                    mHoldSwitchingState = HoldSwapState.INACTIVE;
                 }
                 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
             }
@@ -2744,9 +2800,25 @@
         @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
+            // ImsPhoneCallTracker isn't being informed of the right data enabled state via its
+            // registration, so we'll refresh now.
+            boolean isDataEnabled = false;
+            if (mPhone.getDefaultPhone().getDcTracker(TransportType.WWAN) != null) {
+                isDataEnabled = mPhone.getDefaultPhone().getDcTracker(TransportType.WWAN)
+                        .isDataEnabled();
+            }
+
             if (DBG) {
-                log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
-                        targetAccessTech + ", reasonInfo=" + reasonInfo);
+                log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech="
+                        + targetAccessTech + ", reasonInfo=" + reasonInfo + ", dataEnabled="
+                        + mIsDataEnabled + "/" + isDataEnabled + ", dataMetered="
+                        + mIsViLteDataMetered);
+            }
+            if (mIsDataEnabled != isDataEnabled) {
+                loge("onCallHandover: data enabled state doesn't match! (was=" + mIsDataEnabled
+                        + ", actually=" + isDataEnabled);
+                mIsDataEnabled = isDataEnabled;
             }
 
             // Only consider it a valid handover to WIFI if the source radio tech is known.
@@ -2765,7 +2837,7 @@
                     if (isHandoverToWifi) {
                         removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
 
-                        if (mNotifyHandoverVideoFromLTEToWifi && mHasPerformedStartOfCallHandover) {
+                        if (mNotifyHandoverVideoFromLTEToWifi && mHasAttemptedStartOfCallHandover) {
                             // This is a handover which happened mid-call (ie not the start of call
                             // handover from LTE to WIFI), so we'll notify the InCall UI.
                             conn.onConnectionEvent(
@@ -2791,7 +2863,7 @@
                         conn.setLocalVideoCapable(mIsDataEnabled);
                     }
 
-                    if (mNotifyHandoverVideoFromWifiToLTE &&    mIsDataEnabled) {
+                    if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
                         if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
                             log("onCallHandover :: notifying of WIFI to LTE handover.");
                             conn.onConnectionEvent(
@@ -2808,15 +2880,16 @@
                     if (!mIsDataEnabled && mIsViLteDataMetered) {
                         // Call was downgraded from WIFI to LTE and data is metered; downgrade the
                         // call now.
+                        log("onCallHandover :: data is not enabled; attempt to downgrade.");
                         downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
                     }
                 }
             } else {
                 loge("onCallHandover :: connection null.");
             }
-
-            if (!mHasPerformedStartOfCallHandover) {
-                mHasPerformedStartOfCallHandover = true;
+            // If there's a handover, then we're not in the "start of call" handover phase.
+            if (!mHasAttemptedStartOfCallHandover) {
+                mHasAttemptedStartOfCallHandover = true;
             }
             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
                     TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(),
@@ -2854,8 +2927,8 @@
                     conn.onHandoverToWifiFailed();
                 }
             }
-            if (!mHasPerformedStartOfCallHandover) {
-                mHasPerformedStartOfCallHandover = true;
+            if (!mHasAttemptedStartOfCallHandover) {
+                mHasAttemptedStartOfCallHandover = true;
             }
         }
 
@@ -2930,7 +3003,7 @@
         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
             if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
             removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
-            mHasPerformedStartOfCallHandover = false;
+            mHasAttemptedStartOfCallHandover = false;
             unregisterForConnectivityChanges();
 
             if (imsCall == mUssdSession) {
@@ -2967,8 +3040,8 @@
         }
     };
 
-    private final ImsRegistrationImplBase.Callback mImsRegistrationCallback =
-            new ImsRegistrationImplBase.Callback() {
+    private final ImsMmTelManager.RegistrationCallback mImsRegistrationCallback =
+            new ImsMmTelManager.RegistrationCallback() {
 
                 @Override
                 public void onRegistered(
@@ -3007,13 +3080,14 @@
                 }
             };
 
-    private final ImsFeature.CapabilityCallback mImsCapabilityCallback =
-            new ImsFeature.CapabilityCallback() {
+    private final ImsMmTelManager.CapabilityCallback mImsCapabilityCallback =
+            new ImsMmTelManager.CapabilityCallback() {
                 @Override
-                public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
-                    if (DBG) log("onCapabilitiesStatusChanged: " + config);
+                public void onCapabilitiesStatusChanged(
+                        MmTelFeature.MmTelCapabilities capabilities) {
+                    if (DBG) log("onCapabilitiesStatusChanged: " + capabilities);
                     SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = config;
+                    args.arg1 = capabilities;
                     // Remove any pending updates; they're already stale, so no need to process
                     // them.
                     removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
@@ -3038,14 +3112,15 @@
 
     };
 
-    private final ImsConfigImplBase.Callback mConfigCallback = new ImsConfigImplBase.Callback() {
+    private final ProvisioningManager.Callback mConfigCallback =
+            new ProvisioningManager.Callback() {
         @Override
-        public void onConfigChanged(int item, int value) {
+        public void onProvisioningIntChanged(int item, int value) {
             sendConfigChangedIntent(item, Integer.toString(value));
         }
 
         @Override
-        public void onConfigChanged(int item, String value) {
+        public void onProvisioningStringChanged(int item, String value) {
             sendConfigChangedIntent(item, value);
         }
 
@@ -3229,6 +3304,9 @@
                         // Handover check and its not the foreground call any more.
                         return;
                     }
+                    if (!mHasAttemptedStartOfCallHandover) {
+                        mHasAttemptedStartOfCallHandover = true;
+                    }
                     if (!imsCall.isWifiCall()) {
                         // Call did not handover to wifi, notify of handover failure.
                         ImsPhoneConnection conn = findConnection(imsCall);
@@ -3387,6 +3465,9 @@
             case PENDING_RESUME_FOREGROUND_AFTER_FAILURE:
                 holdSwapState = "PENDING_RESUME_FOREGROUND_AFTER_FAILURE";
                 break;
+            case HOLDING_TO_DIAL_OUTGOING:
+                holdSwapState = "HOLDING_TO_DIAL_OUTGOING";
+                break;
         }
         logi("holdSwapState set to " + holdSwapState + " at " + loc);
     }
@@ -3493,6 +3574,14 @@
         return mIsInEmergencyCall;
     }
 
+    /**
+     * @return true if the IMS capability for the specified registration technology is currently
+     * available.
+     */
+    public boolean isImsCapabilityAvailable(int capability, int regTech) {
+        return (getImsRegistrationTech() == regTech) && mMmTelCapabilities.isCapable(capability);
+    }
+
     public boolean isVolteEnabled() {
         boolean isRadioTechLte = getImsRegistrationTech()
                 == ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
@@ -3889,22 +3978,23 @@
                     Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
                             Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)
                             && !mSupportPauseVideo) {
-
+                log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
+                        + " Downgrade to audio");
                 // If the carrier supports downgrading to voice, then we can simply issue a
                 // downgrade to voice instead of terminating the call.
                 modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
             } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
                 // The carrier supports video pause signalling, so pause the video if we didn't just
                 // lose wifi; in that case just disconnect.
+                log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
+                        + " Pause audio");
                 mShouldUpdateImsConfigOnDisconnect = true;
                 conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
             } else {
+                log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
+                        + " Disconnect call.");
                 // At this point the only choice we have is to terminate the call.
-                try {
-                    imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
-                } catch (ImsException ie) {
-                    loge("Couldn't terminate call " + imsCall);
-                }
+                imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index b707761..851f5fe 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -458,6 +458,9 @@
                 Rlog.d(LOG_TAG, "onDisconnect: no parent");
             }
             synchronized (this) {
+                if (mRttTextHandler != null) {
+                    mRttTextHandler.tearDown();
+                }
                 if (mImsCall != null) mImsCall.close();
                 mImsCall = null;
             }
@@ -1186,7 +1189,7 @@
                 || localCallProfile.mMediaProfile.mAudioQuality
                         == ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB
                 || isEvsCodecHighDef)
-                && remoteCallProfile.mRestrictCause == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE;
+                && remoteCallProfile.getRestrictCause() == ImsCallProfile.CALL_RESTRICT_CAUSE_NONE;
         return isHighDef ? AUDIO_QUALITY_HIGH_DEFINITION : AUDIO_QUALITY_STANDARD;
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 0a571c2..72f3a38 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static android.telephony.ServiceState.STATE_IN_SERVICE;
+
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
@@ -154,17 +156,6 @@
     public static final String UT_BUNDLE_KEY_CLIR = "queryClir";
     public static final String UT_BUNDLE_KEY_SSINFO = "imsSsInfo";
 
-    //***** Calling Line Identity Restriction Constants
-    // The 'm' parameter from TS 27.007 7.7
-    private static final int CLIR_NOT_PROVISIONED                    = 0;
-    private static final int CLIR_PROVISIONED_PERMANENT              = 1;
-    private static final int CLIR_PRESENTATION_RESTRICTED_TEMPORARY  = 3;
-    private static final int CLIR_PRESENTATION_ALLOWED_TEMPORARY     = 4;
-    // The 'n' parameter from TS 27.007 7.7
-    private static final int CLIR_DEFAULT     = 0;
-    private static final int CLIR_INVOCATION  = 1;
-    private static final int CLIR_SUPPRESSION = 2;
-
     //***** Instance Variables
 
     private ImsPhone mPhone;
@@ -1001,9 +992,19 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (mPoundString != null) {
-                Rlog.d(LOG_TAG, "processCode: Sending pound string '"
-                       + mDialingNumber + "' over CS pipe.");
-                throw new CallStateException(Phone.CS_FALLBACK);
+                // We'll normally send USSD over the CS pipe, but if it happens that the CS phone
+                // is out of service, we'll just try over IMS instead.
+                if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
+                        == STATE_IN_SERVICE) {
+                    Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+                            + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
+                    throw new CallStateException(Phone.CS_FALLBACK);
+                } else {
+                    Rlog.i(LOG_TAG, "processCode: CS is out of service, sending ussd string '"
+                            + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
+                    sendUssd(mPoundString);
+                }
+
             } else {
                 Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI");
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -1535,9 +1536,9 @@
                 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
             } else {
                 for (int i = 0, s = infos.length; i < s ; i++) {
-                    if (infos[i].getIcbNum() != null) {
-                        sb.append("Num: " + infos[i].getIcbNum() + " status: "
-                                + infos[i].getStatus() + "\n");
+                    if (infos[i].getIncomingCommunicationBarringNumber() != null) {
+                        sb.append("Num: " + infos[i].getIncomingCommunicationBarringNumber()
+                                + " status: " + infos[i].getStatus() + "\n");
                     } else if (infos[i].getStatus() == 1) {
                         sb.append(mContext.getText(com.android.internal
                                 .R.string.serviceEnabled));
@@ -1572,30 +1573,30 @@
 
             // 'm' parameter.
             switch (clirInfo[1]) {
-                case CLIR_NOT_PROVISIONED:
+                case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED:
                     sb.append(mContext.getText(
                             com.android.internal.R.string.serviceNotProvisioned));
                     mState = State.COMPLETE;
                     break;
-                case CLIR_PROVISIONED_PERMANENT:
+                case ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT:
                     sb.append(mContext.getText(
                             com.android.internal.R.string.CLIRPermanent));
                     mState = State.COMPLETE;
                     break;
-                case CLIR_PRESENTATION_RESTRICTED_TEMPORARY:
+                case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED:
                     // 'n' parameter.
                     switch (clirInfo[0]) {
-                        case CLIR_DEFAULT:
+                        case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
                             mState = State.COMPLETE;
                             break;
-                        case CLIR_INVOCATION:
+                        case ImsSsInfo.CLIR_OUTGOING_INVOCATION:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
                             mState = State.COMPLETE;
                             break;
-                        case CLIR_SUPPRESSION:
+                        case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOnNextCallOff));
                             mState = State.COMPLETE;
@@ -1606,20 +1607,20 @@
                             mState = State.FAILED;
                     }
                     break;
-                case CLIR_PRESENTATION_ALLOWED_TEMPORARY:
+                case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED:
                     // 'n' parameter.
                     switch (clirInfo[0]) {
-                        case CLIR_DEFAULT:
+                        case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
                             mState = State.COMPLETE;
                             break;
-                        case CLIR_INVOCATION:
+                        case ImsSsInfo.CLIR_OUTGOING_INVOCATION:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOffNextCallOn));
                             mState = State.COMPLETE;
                             break;
-                        case CLIR_SUPPRESSION:
+                        case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION:
                             sb.append(mContext.getText(
                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
                             mState = State.COMPLETE;
@@ -1729,27 +1730,27 @@
     }
 
     void parseSsData(ImsSsData ssData) {
-        ImsException ex = (ssData.result != ImsSsData.RESULT_SUCCESS)
-                ? new ImsException(null, ssData.result) : null;
-        mSc = getScStringFromScType(ssData.serviceType);
-        mAction = getActionStringFromReqType(ssData.requestType);
+        ImsException ex = (ssData.getResult() != ImsSsData.RESULT_SUCCESS)
+                ? new ImsException(null, ssData.getResult()) : null;
+        mSc = getScStringFromScType(ssData.getServiceType());
+        mAction = getActionStringFromReqType(ssData.getRequestType());
         Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex);
 
-        switch (ssData.requestType) {
+        switch (ssData.getRequestType()) {
             case ImsSsData.SS_ACTIVATION:
             case ImsSsData.SS_DEACTIVATION:
             case ImsSsData.SS_REGISTRATION:
             case ImsSsData.SS_ERASURE:
-                if ((ssData.result == ImsSsData.RESULT_SUCCESS)
+                if ((ssData.getResult() == ImsSsData.RESULT_SUCCESS)
                         && ssData.isTypeUnConditional()) {
                     /*
                      * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and
                      * ssData.requestType is activate/register and
                      * ServiceClass is Voice/Video/None, turn on voice call forwarding.
                      */
-                    boolean cffEnabled = ((ssData.requestType == ImsSsData.SS_ACTIVATION
-                            || ssData.requestType == ImsSsData.SS_REGISTRATION)
-                            && isServiceClassVoiceVideoOrNone(ssData.serviceClass));
+                    boolean cffEnabled = ((ssData.getRequestType() == ImsSsData.SS_ACTIVATION
+                            || ssData.getRequestType() == ImsSsData.SS_REGISTRATION)
+                            && isServiceClassVoiceVideoOrNone(ssData.getServiceClass()));
 
                     Rlog.d(LOG_TAG, "setCallForwardingFlag cffEnabled: " + cffEnabled);
                     if (mIccRecords != null) {
@@ -1767,29 +1768,29 @@
                 if (ssData.isTypeClir()) {
                     Rlog.d(LOG_TAG, "CLIR INTERROGATION");
                     Bundle clirInfo = new Bundle();
-                    clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfo());
+                    clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfoCompat());
                     onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
                 } else if (ssData.isTypeCF()) {
                     Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
                     onQueryCfComplete(new AsyncResult(null, mPhone
                             .handleCfQueryResult(ssData.getCallForwardInfo()), ex));
                 } else if (ssData.isTypeBarring()) {
-                    onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
+                    onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(),
+                            ex));
                 } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
-                    int[] suppServiceInfo = ssData.getSuppServiceInfo();
-                    ImsSsInfo ssInfo = new ImsSsInfo(suppServiceInfo[0], null);
+                    int[] suppServiceInfo = ssData.getSuppServiceInfoCompat();
+                    ImsSsInfo ssInfo = new ImsSsInfo.Builder(suppServiceInfo[0]).build();
                     Bundle clInfo = new Bundle();
                     clInfo.putParcelable(UT_BUNDLE_KEY_SSINFO, ssInfo);
                     onSuppSvcQueryComplete(new AsyncResult(null, clInfo, ex));
                 } else if (ssData.isTypeIcb()) {
-                    onIcbQueryComplete(new AsyncResult(null, ssData.getImsSpecificSuppServiceInfo(),
-                            ex));
+                    onIcbQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
                 } else {
-                    onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
+                    onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex));
                 }
                 break;
             default:
-                Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.requestType);
+                Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.getRequestType());
                 break;
         }
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
index 3f9e8a8..eff2499 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
+import java.nio.channels.ClosedByInterruptException;
 import java.util.concurrent.CountDownLatch;
 
 public class ImsRttTextHandler extends Handler {
@@ -79,17 +80,16 @@
                 String charsReceived;
                 try {
                     charsReceived = mReaderThreadRttTextStream.read();
+                } catch (ClosedByInterruptException e) {
+                    Rlog.i(LOG_TAG, "RttReaderThread - Thread interrupted. Finishing.");
+                    break;
                 } catch (IOException e) {
                     Rlog.e(LOG_TAG, "RttReaderThread - IOException encountered " +
-                            "reading from in-call: %s", e);
+                            "reading from in-call: ", e);
                     obtainMessage(TEARDOWN).sendToTarget();
                     break;
                 }
                 if (charsReceived == null) {
-                    if (Thread.currentThread().isInterrupted()) {
-                        Rlog.i(LOG_TAG, "RttReaderThread - Thread interrupted. Finishing.");
-                        break;
-                    }
                     Rlog.e(LOG_TAG, "RttReaderThread - Stream closed unexpectedly. Attempt to " +
                             "reinitialize.");
                     obtainMessage(TEARDOWN).sendToTarget();
@@ -145,7 +145,7 @@
                 int numCodepointsBuffered = mBufferedTextToNetwork
                         .codePointCount(0, mBufferedTextToNetwork.length());
                 if (numCodepointsBuffered >= MAX_BUFFERED_CHARACTER_COUNT) {
-                    sendMessageAtFrontOfQueue(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
+                    sendMessage(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
                 } else {
                     sendEmptyMessageDelayed(
                             ATTEMPT_SEND_TO_NETWORK, MAX_BUFFERING_DELAY_MILLIS);
@@ -175,12 +175,13 @@
             case EXPIRE_SENT_CODEPOINT_COUNT:
                 mCodepointsAvailableForTransmission += msg.arg1;
                 if (mCodepointsAvailableForTransmission > 0) {
-                    sendMessageAtFrontOfQueue(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
+                    sendMessage(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
                 }
                 break;
             case TEARDOWN:
                 try {
                     if (mReaderThread != null) {
+                        mReaderThread.interrupt();
                         mReaderThread.join(1000);
                     }
                 } catch (InterruptedException e) {
@@ -202,6 +203,7 @@
     }
 
     public void initialize(Connection.RttTextStream rttTextStream) {
+        Rlog.i(LOG_TAG, "Initializing: " + this);
         obtainMessage(INITIALIZE, rttTextStream).sendToTarget();
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index 5004ce7..45d8061 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -54,6 +54,11 @@
         return this;
     }
 
+    public SmsSessionEventBuilder setImsServiceErrno(int errno) {
+        mEvent.imsError = errno;
+        return this;
+    }
+
     public SmsSessionEventBuilder setSettings(TelephonySettings settings) {
         mEvent.settings = settings;
         return this;
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index a436d25..e06a5c0 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -40,6 +40,8 @@
 import android.os.SystemClock;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
@@ -48,6 +50,7 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
@@ -1451,6 +1454,31 @@
     }
 
     /**
+     * Write SMS related solicited response event
+     *
+     * @param phoneId Phone id
+     * @param errorReason Defined in {@link SmsManager} RESULT_XXX.
+     */
+    public synchronized void writeOnImsServiceSmsSolicitedResponse(int phoneId,
+            @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason) {
+
+        InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
+        if (smsSession == null) {
+            Rlog.e(TAG, "SMS session is missing");
+        } else {
+
+            smsSession.addEvent(new SmsSessionEventBuilder(
+                    SmsSession.Event.Type.SMS_SEND_RESULT_IMS_SERVICE)
+                    .setImsServiceErrno(resultCode)
+                    .setErrorCode(errorReason)
+            );
+
+            smsSession.decreaseExpectedResponse();
+            finishSmsSessionIfNeeded(smsSession);
+        }
+    }
+
+    /**
      * Write deactivate data call response event
      *
      * @param phoneId Phone id
@@ -1735,6 +1763,35 @@
     }
 
     /**
+     * Write Send SMS event using ImsService. Expecting response from
+     * {@link #writeOnSmsSolicitedResponse}.
+     *
+     * @param phoneId Phone id
+     * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+     * {@link SmsMessage#FORMAT_3GPP2}.
+     */
+    public synchronized void writeImsServiceSendSms(int phoneId, String format) {
+        InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
+        int formatCode = SmsSession.Event.Format.SMS_FORMAT_UNKNOWN;
+        switch (format) {
+            case SmsMessage.FORMAT_3GPP : {
+                formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP;
+                break;
+            }
+            case SmsMessage.FORMAT_3GPP2: {
+                formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP2;
+                break;
+            }
+        }
+        smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND_IMS_SERVICE)
+                .setTech(SmsSession.Event.Tech.SMS_IMS)
+                .setFormat(formatCode)
+        );
+
+        smsSession.increaseExpectedResponse();
+    }
+
+    /**
      * Write incoming SMS event
      *
      * @param phoneId Phone id
@@ -1753,6 +1810,35 @@
     }
 
     /**
+     * Write incoming SMS event
+     *
+     * @param phoneId Phone id
+     * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+     * {@link SmsMessage#FORMAT_3GPP2}.
+     */
+    public synchronized void writeImsServiceNewSms(int phoneId, String format) {
+        InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
+        int formatCode = SmsSession.Event.Format.SMS_FORMAT_UNKNOWN;
+        switch (format) {
+            case SmsMessage.FORMAT_3GPP : {
+                formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP;
+                break;
+            }
+            case SmsMessage.FORMAT_3GPP2: {
+                formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP2;
+                break;
+            }
+        }
+        smsSession.addEvent(new SmsSessionEventBuilder(
+                SmsSession.Event.Type.SMS_RECEIVED_IMS_SERVICE)
+                .setTech(SmsSession.Event.Tech.SMS_IMS)
+                .setFormat(formatCode)
+        );
+
+        finishSmsSessionIfNeeded(smsSession);
+    }
+
+    /**
      * Write incoming Broadcast SMS event
      *
      * @param phoneId Phone id
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 03167bc..272187e 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -441,7 +441,7 @@
     }
 
     @Override
-    public boolean isDataAllowed() {
+    public boolean isDataAllowed(int apnType) {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index e3c147b..9a87d01 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -39,6 +39,7 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 
@@ -158,7 +159,7 @@
 
         simulatedCallState = new SimulatedGsmCallState(looper);
 
-        setRadioState(RadioState.RADIO_ON, false /* forceNotifyRegistrants */);
+        setRadioState(TelephonyManager.RADIO_POWER_ON, false /* forceNotifyRegistrants */);
         mSimLockedState = INITIAL_LOCK_STATE;
         mSimLockEnabled = (mSimLockedState != SimLockState.NONE);
         mPinCode = DEFAULT_SIM_PIN_CODE;
@@ -505,7 +506,7 @@
     @Override
     public void getCurrentCalls (Message result) {
         SimulatedCommandsVerifier.getInstance().getCurrentCalls(result);
-        if ((mState == RadioState.RADIO_ON) && !isSimLocked()) {
+        if ((mState == TelephonyManager.RADIO_POWER_ON) && !isSimLocked()) {
             //Rlog.i("GSM", "[SimCmds] getCurrentCalls");
             resultSuccess(result, simulatedCallState.getDriverCalls());
         } else {
@@ -1241,9 +1242,9 @@
         }
 
         if(on) {
-            setRadioState(RadioState.RADIO_ON, false /* forceNotifyRegistrants */);
+            setRadioState(TelephonyManager.RADIO_POWER_ON, false /* forceNotifyRegistrants */);
         } else {
-            setRadioState(RadioState.RADIO_OFF, false /* forceNotifyRegistrants */);
+            setRadioState(TelephonyManager.RADIO_POWER_OFF, false /* forceNotifyRegistrants */);
         }
         resultSuccess(result, null);
     }
@@ -1599,7 +1600,7 @@
     @Override
     public void
     shutdown() {
-        setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
+        setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE, false /* forceNotifyRegistrants */);
         Looper looper = mHandlerThread.getLooper();
         if (looper != null) {
             looper.quit();
@@ -2024,7 +2025,7 @@
 
     @Override
     public void requestShutdown(Message result) {
-        setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
+        setRadioState(TelephonyManager.RADIO_POWER_UNAVAILABLE, false /* forceNotifyRegistrants */);
     }
 
     @Override
@@ -2294,4 +2295,8 @@
     public void stopNattKeepalive(int sessionHandle, Message result) {
         SimulatedCommandsVerifier.getInstance().stopNattKeepalive(sessionHandle, result);
     }
+
+    public Handler getHandler() {
+        return mHandlerThread.getThreadHandler();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index 12e27e1..4460489 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -22,6 +22,7 @@
 import android.os.Message;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
 
 import com.android.internal.telephony.CommandsInterface;
@@ -45,8 +46,8 @@
     }
 
     @Override
-    public RadioState getRadioState() {
-        return null;
+    public int getRadioState() {
+        return TelephonyManager.RADIO_POWER_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/uicc/AnswerToReset.java b/src/java/com/android/internal/telephony/uicc/AnswerToReset.java
index b94df6d..6fd7c68 100644
--- a/src/java/com/android/internal/telephony/uicc/AnswerToReset.java
+++ b/src/java/com/android/internal/telephony/uicc/AnswerToReset.java
@@ -38,10 +38,10 @@
     private static final boolean VDBG = false; // STOPSHIP if true
     private static final int TAG_CARD_CAPABILITIES = 0x07;
     private static final int EXTENDED_APDU_INDEX = 2;
+    private static final int B8_MASK = 0x80;
     private static final int B7_MASK = 0x40;
     private static final int B2_MASK = 0x02;
 
-    public static final byte EUICC_SUPPORTED = (byte) 0x82;
     public static final byte DIRECT_CONVENTION = (byte) 0x3B;
     public static final byte INVERSE_CONVENTION = (byte) 0x3F;
     public static final int INTERFACE_BYTES_MASK = 0xF0;
@@ -148,12 +148,13 @@
     }
 
     private void checkIsEuiccSupported() {
-        // eUICC is supported only if the value of the first tB after T=15 is 82.
+        // eUICC is supported only if the b8 and b2 of the first tB after T=15 are set to 1.
         for (int i = 0; i < mInterfaceBytes.size() - 1; i++) {
             if (mInterfaceBytes.get(i).getTD() != null
                     && (mInterfaceBytes.get(i).getTD() & T_MASK) == T_VALUE_FOR_GLOBAL_INTERFACE
                     && mInterfaceBytes.get(i + 1).getTB() != null
-                    && mInterfaceBytes.get(i + 1).getTB() == EUICC_SUPPORTED) {
+                    && (mInterfaceBytes.get(i + 1).getTB() & B8_MASK) != 0
+                    && (mInterfaceBytes.get(i + 1).getTB() & B2_MASK) != 0) {
                 mIsEuiccSupported = true;
                 return;
             }
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index 3998eb2..5e7abfd 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -67,6 +67,7 @@
     public int        physicalSlotIndex = UiccController.INVALID_SLOT_ID;
     public String     atr;
     public String     iccid;
+    public String     eid;
 
     public IccCardApplicationStatus[] mApplications;
 
@@ -120,8 +121,12 @@
 
         StringBuilder sb = new StringBuilder();
         sb.append("IccCardState {").append(mCardState).append(",")
-        .append(mUniversalPinState)
-        .append(",num_apps=").append(mApplications.length);
+        .append(mUniversalPinState);
+        if (mApplications != null) {
+            sb.append(",num_apps=").append(mApplications.length);
+        } else {
+            sb.append(",mApplications=null");
+        }
 
         sb.append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
         if (mApplications != null
@@ -149,6 +154,7 @@
 
         sb.append(",physical_slot_id=").append(physicalSlotIndex).append(",atr=").append(atr);
         sb.append(",iccid=").append(SubscriptionInfo.givePrintableIccid(iccid));
+        sb.append(",eid=").append(eid);
 
         sb.append("}");
         return sb.toString();
diff --git a/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
index 5fdd322..43f525e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccSlotStatus.java
@@ -34,6 +34,7 @@
     public int        logicalSlotIndex;
     public String     atr;
     public String     iccid;
+    public String     eid;
 
     /**
      * Set the cardState according to the input state.
@@ -80,7 +81,8 @@
                 .append(slotState).append(",")
                 .append("logicalSlotIndex=").append(logicalSlotIndex).append(",")
                 .append("atr=").append(atr).append(",iccid=")
-                .append(SubscriptionInfo.givePrintableIccid(iccid));
+                .append(SubscriptionInfo.givePrintableIccid(iccid)).append(",")
+                .append("eid=").append(eid);
 
         sb.append("}");
         return sb.toString();
@@ -100,7 +102,8 @@
                 && (slotState == that.slotState)
                 && (logicalSlotIndex == that.logicalSlotIndex)
                 && (TextUtils.equals(atr, that.atr))
-                && (TextUtils.equals(iccid, that.iccid));
+                && (TextUtils.equals(iccid, that.iccid))
+                && (TextUtils.equals(eid, that.eid));
     }
 
 }
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 4f44f15..ba10eff 100755
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -1028,16 +1028,23 @@
                                         onCphsCompleted));
                     } else {
                         if (ar.userObj != null) {
-                            CarrierConfigManager configLoader = (CarrierConfigManager)
+                            CarrierConfigManager configManager = (CarrierConfigManager)
                                     mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-                            if (ar.exception != null && configLoader != null
-                                    && configLoader.getConfig().getBoolean(
-                                    CarrierConfigManager.KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL)) {
-                                // GsmCdmaPhone will store vm number on device
-                                // when IccVmNotSupportedException occurred
-                                AsyncResult.forMessage(((Message) ar.userObj)).exception =
-                                        new IccVmNotSupportedException(
-                                            "Update SIM voice mailbox error");
+                            if (ar.exception != null && configManager != null) {
+                                PersistableBundle b = configManager.getConfigForSubId(
+                                        SubscriptionController.getInstance().getSubIdUsingPhoneId(
+                                                mParentApp.getPhoneId()));
+                                if (b != null && b.getBoolean(
+                                        CarrierConfigManager.KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL)) {
+                                    // GsmCdmaPhone will store vm number on device
+                                    // when IccVmNotSupportedException occurred
+                                    AsyncResult.forMessage(((Message) ar.userObj)).exception =
+                                            new IccVmNotSupportedException(
+                                                    "Update SIM voice mailbox error");
+                                } else {
+                                    AsyncResult.forMessage(((Message) ar.userObj))
+                                            .exception = ar.exception;
+                                }
                             } else {
                                 AsyncResult.forMessage(((Message) ar.userObj))
                                     .exception = ar.exception;
@@ -1401,6 +1408,7 @@
 
     private void onLockedAllRecordsLoaded() {
         setSimLanguageFromEF();
+        setVoiceCallForwardingFlagFromSimRecords();
         if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
             mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
         } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 361f70b..aae7874 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -47,8 +47,8 @@
             "com.android.internal.telephony.uicc.ICC_CARD_ADDED";
 
     // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
-    // between UiccSlot, UiccCard and UiccProfile for now.
-    private final Object mLock;
+    // between UiccSlot, UiccCard, EuiccCard, and UiccProfile for now.
+    protected final Object mLock;
     private CardState mCardState;
     private String mIccid;
     protected String mCardId;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index d9abe13..76a0227 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -312,7 +312,8 @@
             // gained carrier privileges (as an indication that a matching SIM has been inserted).
             PackageInfo pInfo = packageManager.getPackageInfo(packageName,
                     PackageManager.GET_SIGNATURES
-                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS);
+                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                            | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS);
             return getCarrierPrivilegeStatus(pInfo);
         } catch (PackageManager.NameNotFoundException ex) {
             log("Package " + packageName + " not found for carrier privilege status check");
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 0656831..cb860d8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -25,11 +26,14 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.storage.StorageManager;
+import android.preference.PreferenceManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.LocalLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccCardConstants;
@@ -37,6 +41,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RadioConfig;
 import com.android.internal.telephony.SubscriptionInfoUpdater;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -112,16 +117,26 @@
 
     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
     private CommandsInterface[] mCis;
-    private UiccSlot[] mUiccSlots;
+    @VisibleForTesting
+    public UiccSlot[] mUiccSlots;
     private int[] mPhoneIdToSlotId;
     private boolean mIsSlotStatusSupported = true;
-    private boolean mIsCdmaSupported = true;
+
+    // This maps the externally exposed card ID (int) to the internal card ID string (ICCID/EID).
+    // The array index is the card ID (int).
+    // This mapping exists to expose card-based functionality without exposing the EID, which is
+    // considered sensetive information.
+    private ArrayList<String> mCardStrings;
+
+    // SharedPreference key for saving the known card strings (ICCIDs and EIDs) ordered by card ID
+    private static final String CARD_STRINGS = "card_strings";
 
     private static final Object mLock = new Object();
     private static UiccController mInstance;
     private static ArrayList<IccSlotStatus> sLastSlotStatus;
 
-    private Context mContext;
+    @VisibleForTesting
+    public Context mContext;
 
     protected RegistrantList mIccChangedRegistrants = new RegistrantList();
 
@@ -183,11 +198,7 @@
         }
 
         mLauncher = new UiccStateChangedLauncher(c, this);
-
-        // set mIsCdmaSupported based on PackageManager.FEATURE_TELEPHONY_CDMA
-        PackageManager packageManager = c.getPackageManager();
-        mIsCdmaSupported =
-                packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
+        mCardStrings = loadCardStrings();
     }
 
     private int getSlotIdFromPhoneId(int phoneId) {
@@ -536,10 +547,52 @@
 
         mUiccSlots[slotId].update(mCis[index], status, index);
 
+        UiccCard card = mUiccSlots[slotId].getUiccCard();
+        if (card != null && (card.getCardState() == CardState.CARDSTATE_PRESENT)) {
+            // Card.getCardId returns the cardString, not the public card ID int
+            String cardString = card.getCardId();
+            addCardId(cardString);
+        }
+
         if (DBG) log("Notifying IccChangedRegistrants");
         mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
     }
 
+    private void addCardId(String cardString) {
+        if (TextUtils.isEmpty(cardString)) {
+            return;
+        }
+        if (!mCardStrings.contains(cardString)) {
+            mCardStrings.add(cardString);
+            saveCardStrings();
+        }
+    }
+
+    /**
+     * Converts the card string (the ICCID/EID, formerly named card ID) to the public int cardId.
+     * Returns -1 if the card string does not map to a cardId.
+     */
+    public int convertToPublicCardId(String cardString) {
+        return mCardStrings.indexOf(cardString);
+    }
+
+    private ArrayList<String> loadCardStrings() {
+        String cardStrings =
+                PreferenceManager.getDefaultSharedPreferences(mContext).getString(CARD_STRINGS, "");
+        if (TextUtils.isEmpty(cardStrings)) {
+            // just return an empty list, since String.split would return the list { "" }
+            return new ArrayList<String>();
+        }
+        return new ArrayList<String>(Arrays.asList(cardStrings.split(",")));
+    }
+
+    private void saveCardStrings() {
+        SharedPreferences.Editor editor =
+                PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+        editor.putString(CARD_STRINGS, TextUtils.join(",", mCardStrings));
+        editor.commit();
+    }
+
     private synchronized void onGetSlotStatusDone(AsyncResult ar) {
         if (!mIsSlotStatusSupported) {
             if (VDBG) log("onGetSlotStatusDone: ignoring since mIsSlotStatusSupported is false");
@@ -598,6 +651,11 @@
             }
 
             mUiccSlots[i].update(isActive ? mCis[iss.logicalSlotIndex] : null, iss);
+
+            if (mUiccSlots[i].isEuicc()) {
+                String eid = iss.eid;
+                addCardId(eid);
+            }
         }
 
         if (VDBG) logPhoneIdToSlotIdMapping();
@@ -702,8 +760,16 @@
         mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
     }
 
-    public boolean isCdmaSupported() {
-        return mIsCdmaSupported;
+    /**
+     * static method to return whether CDMA is supported on the device
+     * @param context object representative of the application that is calling this method
+     * @return true if CDMA is supported by the device
+     */
+    public static boolean isCdmaSupported(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        boolean isCdmaSupported =
+                packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
+        return isCdmaSupported;
     }
 
     private boolean isValidPhoneIndex(int index) {
@@ -733,7 +799,7 @@
         }
         pw.println();
         pw.flush();
-        pw.println(" mIsCdmaSupported=" + mIsCdmaSupported);
+        pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
         pw.println(" mUiccSlots: size=" + mUiccSlots.length);
         for (int i = 0; i < mUiccSlots.length; i++) {
             if (mUiccSlots[i] == null) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 78081b8..ce4a5ef 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -64,6 +64,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -204,6 +205,7 @@
 
                 case EVENT_CARRIER_CONFIG_CHANGED:
                     handleCarrierNameOverride();
+                    handleSimCountryIsoOverride();
                     break;
 
                 case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
@@ -241,6 +243,7 @@
         }
 
         if (mUiccCard instanceof EuiccCard) {
+            // for RadioConfig<1.1 eid is not known when the EuiccCard is constructed
             ((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null);
         }
 
@@ -351,6 +354,41 @@
         updateCarrierNameForSubscription(subCon, subId);
     }
 
+    /**
+     * Override sim country iso based on carrier config.
+     * Telephony country iso is based on MCC table which is coarse and doesn't work with dual IMSI
+     * SIM. e.g, a US carrier might have a roaming agreement with carriers from Europe. Devices
+     * will switch to different IMSI (differnt mccmnc) when enter roaming state. As a result, sim
+     * country iso (locale) will change to non-US.
+     *
+     * Each sim carrier should have a single country code. We should improve the accuracy of
+     * SIM country code look-up by using carrierid-to-countrycode table as an override on top of
+     * MCC table
+     */
+    private void handleSimCountryIsoOverride() {
+        SubscriptionController subCon = SubscriptionController.getInstance();
+        final int subId = subCon.getSubIdUsingPhoneId(mPhoneId);
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            loge("subId not valid for Phone " + mPhoneId);
+            return;
+        }
+
+        CarrierConfigManager configLoader = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configLoader == null) {
+            loge("Failed to load a Carrier Config");
+            return;
+        }
+
+        PersistableBundle config = configLoader.getConfigForSubId(subId);
+        String iso = config.getString(CarrierConfigManager.KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING);
+        if (!TextUtils.isEmpty(iso) &&
+                !iso.equals(mTelephonyManager.getSimCountryIsoForPhone(mPhoneId))) {
+            mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, iso);
+            SubscriptionController.getInstance().setCountryIso(iso, subId);
+        }
+    }
+
     private void updateCarrierNameForSubscription(SubscriptionController subCon, int subId) {
         /* update display name with carrier override */
         SubscriptionInfo subInfo = subCon.getActiveSubscriptionInfo(
@@ -422,6 +460,7 @@
         }
 
         if (mUiccCard instanceof EuiccCard && ((EuiccCard) mUiccCard).getEid() == null) {
+            // for RadioConfig<1.1 the EID is not known when the EuiccCard is constructed
             if (DBG) log("EID is not ready yet.");
             return;
         }
@@ -960,7 +999,7 @@
         // TODO: 2/15/18 Add check to see if ISIM app will go to READY state, and if yes, check for
         // ISIM also (currently ISIM is considered as not supported in this function)
         if (app.getType() == AppType.APPTYPE_USIM || app.getType() == AppType.APPTYPE_SIM
-                || (UiccController.getInstance().isCdmaSupported()
+                || (UiccController.isCdmaSupported(mContext)
                 && (app.getType() == AppType.APPTYPE_CSIM
                 || app.getType() == AppType.APPTYPE_RUIM))) {
             return true;
@@ -1464,6 +1503,24 @@
     }
 
     /**
+     * Return a list of certs in hex string from loaded carrier privileges access rules.
+     *
+     * @return a list of certificate in hex string. return {@code null} if there is no certs
+     * or privilege rules are not loaded yet.
+     */
+    public List<String> getCertsFromCarrierPrivilegeAccessRules() {
+        final List<String> certs = new ArrayList<>();
+        final UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        if (carrierPrivilegeRules != null) {
+            List<UiccAccessRule> accessRules = carrierPrivilegeRules.getAccessRules();
+            for (UiccAccessRule accessRule : accessRules) {
+                certs.add(accessRule.getCertificateHexString());
+            }
+        }
+        return certs.isEmpty() ? null : certs;
+    }
+
+    /**
      * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
      */
     public List<String> getCarrierPackageNamesForIntent(
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index 0fa71c3..fcc60a1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -27,11 +27,12 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.CommandsInterface.RadioState;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.euicc.EuiccCard;
@@ -57,7 +58,7 @@
     private Context mContext;
     private CommandsInterface mCi;
     private UiccCard mUiccCard;
-    private RadioState mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+    private int mLastRadioState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
     private boolean mIsEuicc;
     private String mIccId;
     private AnswerToReset mAtr;
@@ -86,7 +87,7 @@
             parseAtr(ics.atr);
             mCi = ci;
 
-            RadioState radioState = mCi.getRadioState();
+            int radioState = mCi.getRadioState();
             if (DBG) {
                 log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
             }
@@ -100,7 +101,8 @@
             } else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT
                     || mUiccCard == null) && mCardState != CardState.CARDSTATE_ABSENT) {
                 // No notifications while radio is off or we just powering up
-                if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+                if (radioState == TelephonyManager.RADIO_POWER_ON
+                        && mLastRadioState == TelephonyManager.RADIO_POWER_ON) {
                     if (DBG) log("update: notify card added");
                     sendMessage(obtainMessage(EVENT_CARD_ADDED, null));
                 }
@@ -114,6 +116,11 @@
                 if (!mIsEuicc) {
                     mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
                 } else {
+                    // The EID should be reported with the card status, but in case it's not we want
+                    // to catch that here
+                    if (TextUtils.isEmpty(ics.eid)) {
+                        loge("update: eid is missing. ics.eid=" + ics.eid);
+                    }
                     mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
                 }
             } else {
@@ -141,7 +148,7 @@
                 // even if it's inactive.
                 if (mActive) {
                     mActive = false;
-                    mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+                    mLastRadioState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
                     mPhoneId = INVALID_PHONE_ID;
                     if (mUiccCard != null) mUiccCard.dispose();
                     nullifyUiccCard(true /* sim state is unknown */);
@@ -165,10 +172,11 @@
     }
 
     private void updateCardStateAbsent() {
-        RadioState radioState =
-                (mCi == null) ? RadioState.RADIO_UNAVAILABLE : mCi.getRadioState();
+        int radioState =
+                (mCi == null) ? TelephonyManager.RADIO_POWER_UNAVAILABLE : mCi.getRadioState();
         // No notifications while radio is off or we just powering up
-        if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+        if (radioState == TelephonyManager.RADIO_POWER_ON
+                && mLastRadioState == TelephonyManager.RADIO_POWER_ON) {
             if (DBG) log("update: notify card removed");
             sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
         }
@@ -366,7 +374,7 @@
         }
 
         mCardState = CardState.CARDSTATE_ABSENT;
-        mLastRadioState = RadioState.RADIO_UNAVAILABLE;
+        mLastRadioState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
     }
 
     private void log(String msg) {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index b5083a1..094a6f3 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -112,7 +112,6 @@
     }
 
     private final ApduSender mApduSender;
-    private final Object mLock = new Object();
     private RegistrantList mEidReadyRegistrants;
     private EuiccSpecVersion mSpecVersion;
     private volatile String mEid;
@@ -122,7 +121,13 @@
         // TODO: Set supportExtendedApdu based on ATR.
         mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
 
-        loadEidAndNotifyRegistrants();
+        if (TextUtils.isEmpty(ics.eid)) {
+            loge("no eid given in constructor for phone " + phoneId);
+            loadEidAndNotifyRegistrants();
+        } else {
+            mEid = ics.eid;
+            mCardId = ics.eid;
+        }
     }
 
     /**
@@ -150,6 +155,8 @@
         }
     }
 
+    // For RadioConfig<1.1 we don't know the EID when constructing the EuiccCard, so callers may
+    // need to register to be notified when we have the EID
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected void loadEidAndNotifyRegistrants() {
         Handler euiccMainThreadHandler = new Handler();
@@ -190,6 +197,16 @@
     }
 
     @Override
+    public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
+        synchronized (mLock) {
+            if (!TextUtils.isEmpty(ics.eid)) {
+                mEid = ics.eid;
+            }
+            super.update(c, ci, ics);
+        }
+    }
+
+    @Override
     protected void updateCardId() {
         if (TextUtils.isEmpty(mEid)) {
             super.updateCardId();
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index f772c49..3a8a1e6 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -4,20 +4,21 @@
     srcs: ["**/*.java"],
 
     libs: [
-        "android.test.runner",
-        "ims-common",
         "android.test.base",
         "android.test.mock",
+        "android.test.runner",
+        "ims-common",
     ],
 
     static_libs: [
-        "guava",
+        "android-support-test",
         "frameworks-base-testutils",
+        "guava",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
         "services.core",
         "telephony-common",
-        "mockito-target-minus-junit4",
-        "android-support-test",
-        "platform-test-annotations",
+        "truth-prebuilt",
     ],
 
     platform_apis: true,
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index 07d3223..b7d2998 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -23,7 +23,9 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.Parcel;
+import android.os.RemoteException;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.feature.CapabilityChangeRequest;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
@@ -42,9 +44,28 @@
 
 @RunWith(AndroidJUnit4.class)
 public class ImsFeatureTest {
+    // Public for Mockito testing
+    public class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public void onCapabilitiesStatusChanged(int config) throws RemoteException {
+
+        }
+    }
 
     private TestImsFeature mTestImsFeature;
-    private ImsFeature.CapabilityCallback mCapabilityCallback;
 
     @Mock
     private IImsFeatureStatusCallback mTestStatusCallback;
@@ -55,14 +76,11 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mTestImsFeature = new TestImsFeature();
-        mCapabilityCallback = Mockito.spy(new ImsFeature.CapabilityCallback());
-        mTestImsFeature.addCapabilityCallback(mCapabilityCallback);
     }
 
     @After
     public void tearDown() {
         mTestImsFeature = null;
-        mCapabilityCallback = null;
     }
 
     @Test
@@ -145,14 +163,17 @@
     @SmallTest
     @Test
     public void testSetCapabilityConfigError() throws Exception {
+        CapabilityCallback capabilityCallback = Mockito.spy(new CapabilityCallback());
+        mTestImsFeature.addCapabilityCallback(capabilityCallback);
+
         CapabilityChangeRequest request = new CapabilityChangeRequest();
         request.addCapabilitiesToEnableForTech(TestImsFeature.CAPABILITY_TEST_1,
                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
 
         mTestImsFeature.setCapabilitiesResult = ImsFeature.CAPABILITY_ERROR_GENERIC;
-        mTestImsFeature.requestChangeEnabledCapabilities(request, mCapabilityCallback);
+        mTestImsFeature.requestChangeEnabledCapabilities(request, capabilityCallback);
 
-        verify(mCapabilityCallback).onChangeCapabilityConfigurationError(
+        verify(capabilityCallback).onChangeCapabilityConfigurationError(
                 eq(TestImsFeature.CAPABILITY_TEST_1),
                 eq(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN),
                 eq(ImsFeature.CAPABILITY_ERROR_GENERIC));
@@ -175,6 +196,9 @@
     @SmallTest
     @Test
     public void testNotifyCapabilityStatusChangedCallback() throws Exception {
+        CapabilityCallback capabilityCallback = Mockito.spy(new CapabilityCallback());
+        mTestImsFeature.addCapabilityCallback(capabilityCallback);
+
         ImsFeature.Capabilities status =
                 new ImsFeature.Capabilities();
         status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
@@ -183,7 +207,8 @@
         mTestImsFeature.capabilitiesStatusChanged(status);
 
         assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
-        verify(mCapabilityCallback).onCapabilitiesStatusChanged(eq(status));
+        verify(capabilityCallback).onCapabilitiesStatusChanged(
+                eq(TestImsFeature.CAPABILITY_TEST_1 | TestImsFeature.CAPABILITY_TEST_2));
     }
 
     @SmallTest
@@ -231,4 +256,23 @@
 
         assertEquals(request, result);
     }
+
+    @SmallTest
+    @Test
+    public void testCapabilityCallbackWhenRegistering() throws Exception {
+        CapabilityCallback capabilityCallback = Mockito.spy(new CapabilityCallback());
+
+        // Signal the status has changed
+        ImsFeature.Capabilities status =
+                new ImsFeature.Capabilities();
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_1);
+        status.addCapabilities(TestImsFeature.CAPABILITY_TEST_2);
+        mTestImsFeature.capabilitiesStatusChanged(status);
+
+        // addCapabilityCallback should cause capabilityCallback to call back with status.
+        mTestImsFeature.addCapabilityCallback(capabilityCallback);
+        assertEquals(status.getMask(), mTestImsFeature.queryCapabilityStatus().getMask());
+        verify(capabilityCallback).onCapabilitiesStatusChanged(
+                eq(TestImsFeature.CAPABILITY_TEST_1 | TestImsFeature.CAPABILITY_TEST_2));
+    }
 }
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
new file mode 100644
index 0000000..404e584
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.telephony.ims;
+
+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.verify;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class ImsMmTelManagerTests extends TelephonyTest {
+
+    @Mock
+    IBinder mMockBinder;
+    @Mock
+    ITelephony mMockTelephonyInterface;
+
+    public class LocalCallback extends ImsMmTelManager.RegistrationCallback {
+        int mRegResult = -1;
+
+        @Override
+        public void onRegistered(int imsRadioTech) {
+            mRegResult = imsRadioTech;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp("ImsMmTelManagerTests");
+        doReturn(mMockTelephonyInterface).when(mMockBinder).queryLocalInterface(anyString());
+        mServiceManagerMockedServices.put("phone", mMockBinder);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Ensure that LTE-> WWAN and IWLAN-> WLAN map correctly as well as ensure that wacky values
+     * result in a -1 result.
+     */
+    @SmallTest
+    @Test
+    public void testCallbackValues() throws RemoteException {
+        LocalCallback cb = new LocalCallback();
+        ImsMmTelManager managerUT = new ImsMmTelManager(0);
+        managerUT.registerImsRegistrationCallback(Runnable::run, cb);
+        // Capture the RegistrationCallback that was registered.
+        ArgumentCaptor<IImsRegistrationCallback> callbackCaptor =
+                ArgumentCaptor.forClass(IImsRegistrationCallback.class);
+        verify(mMockTelephonyInterface).registerImsRegistrationCallback(anyInt(),
+                callbackCaptor.capture());
+
+        IImsRegistrationCallback cbBinder = callbackCaptor.getValue();
+        // Ensure the transport types are correct
+        cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        assertEquals(AccessNetworkConstants.TransportType.WWAN, cb.mRegResult);
+        cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+        assertEquals(AccessNetworkConstants.TransportType.WLAN, cb.mRegResult);
+        cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+        assertEquals(-1, cb.mRegResult);
+        // Wacky value
+        cbBinder.onRegistered(0xDEADBEEF);
+        assertEquals(-1, cb.mRegResult);
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index 6be2fb0..79b9d60 100644
--- a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -30,8 +30,8 @@
 import android.os.Messenger;
 import android.support.test.runner.AndroidJUnit4;
 import android.telecom.TelecomManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -49,6 +49,26 @@
 @RunWith(AndroidJUnit4.class)
 public class MmTelFeatureTests extends ImsTestBase {
 
+    // Public for Mockito testing
+    public class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled) {
+
+        }
+
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                int reason) {
+
+        }
+
+        @Override
+        public void onCapabilitiesStatusChanged(int config) {
+
+        }
+    }
+
     private static final int TEST_CAPABILITY = 1;
     private static final int TEST_RADIO_TECH = 0;
 
@@ -60,7 +80,7 @@
 
     private android.telephony.ims.TestMmTelFeature mFeature;
     private IImsMmTelFeature mFeatureBinder;
-    private ImsFeature.CapabilityCallback mCapabilityCallback;
+    private CapabilityCallback mCapabilityCallback;
     private MmTelFeature.Listener mListener;
 
     // set to true when the handler receives a message back from the Feature.
@@ -92,7 +112,7 @@
         super.setUp();
         mFeature = new TestMmTelFeature();
         mFeatureBinder = mFeature.getBinder();
-        mCapabilityCallback = spy(new ImsFeature.CapabilityCallback());
+        mCapabilityCallback = spy(new CapabilityCallback());
         mListener = spy(new MmTelFeature.Listener());
         mFeatureBinder.setListener(mListener);
         mHandlerResults = new boolean[TEST_RESULT_MAX];
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index 6414403..fc1d670 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.feature.CapabilityChangeRequest;
@@ -50,8 +51,8 @@
         }
     }
 
-    public void incomingCall(ImsCallSessionImplBase c) throws RemoteException {
-        notifyIncomingCall(c, null);
+    public void incomingCall(ImsCallSessionImplBase c) {
+        notifyIncomingCall(c, new Bundle());
     }
 
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
index 54a56df..59c36bb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
@@ -87,15 +87,18 @@
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_MissingApp() throws Exception {
         Mockito.when(mPackageManager.getApplicationInfo("com.example.missing.app",
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(null);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(null);
         ArraySet<String> systemCarrierAppsDisabledUntilUsed = new ArraySet<>();
         systemCarrierAppsDisabledUntilUsed.add("com.example.missing.app");
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID,
                 systemCarrierAppsDisabledUntilUsed, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppHiddenUntilInstalled(
+                Mockito.anyString(), Mockito.anyBoolean());
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(Mockito.any(String[].class),
                         Mockito.anyInt());
@@ -107,12 +110,15 @@
     public void testDisableCarrierAppsUntilPrivileged_NonSystemApp() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppHiddenUntilInstalled(
+                Mockito.anyString(), Mockito.anyBoolean());
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -128,17 +134,20 @@
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
@@ -148,37 +157,43 @@
     public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Disabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
 
-    /** Configured app has privileges, and is already enabled - should only grant permissions. */
+    /** Configured app has privileges, and is already installed - should only grant permissions. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Enabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
@@ -188,23 +203,27 @@
     public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_UpdatedApp() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
+                | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
 
     /**
-     * Configured app has privileges, and is in the default state - should enable. Associated app
+     * Configured app has privileges, and is in the default state - should install. Associated app
      * is missing and should not be touched.
      */
     @Test @SmallTest
@@ -212,28 +231,29 @@
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, true, USER_ID);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                ASSOCIATED_APP, true, USER_ID);
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
 
     /**
      * Configured app has privileges, and is in the default state along with associated app - should
-     * enable both.
+     * install both.
      */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Associated_Default()
@@ -243,34 +263,38 @@
                 mContentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, USER_ID);
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
         associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         associatedAppInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, true, USER_ID);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                ASSOCIATED_APP, true, USER_ID);
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
 
     /**
-     * Configured app has privileges, and is disabled until used - should enable. Associated app has
+     * Configured app has privileges, and is uninstalled - should install. Associated app has
      * been updated and should not be touched.
      */
     @Test @SmallTest
@@ -278,10 +302,12 @@
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
         associatedAppInfo.flags |=
@@ -289,53 +315,61 @@
         associatedAppInfo.enabledSetting =
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, true, USER_ID);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                ASSOCIATED_APP, true, USER_ID);
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
 
     /**
-     * Configured app has privileges, and is disabled until used along with associated app - should
-     * enable both.
+     * Configured app has privileges, and is uninstalled until used along with associated app -
+     * should install both.
      */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Associated_DisabledUntilUsed()
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
         associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         associatedAppInfo.enabledSetting =
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
-                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, true, USER_ID);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                ASSOCIATED_APP, true, USER_ID);
         Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
                 new String[] {appInfo.packageName}, USER_ID);
     }
@@ -345,17 +379,20 @@
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUser() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -367,98 +404,113 @@
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Configured app has no privileges, and was disabled - should do nothing. */
+    /** Configured app has no privileges, and was uninstalled - should do nothing. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Disabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Telephony is not initialized, and app was disabled - should do nothing. */
+    /** Telephony is not initialized, and app was uninstalled - should do nothing. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Disabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Configured app has no privileges, and is explicitly enabled - should do nothing. */
+    /** Configured app has no privileges, and is explicitly installed - should do nothing. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Enabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Telephony is not initialized, and app is explicitly enabled - should do nothing. */
+    /** Telephony is not initialized, and app is explicitly installed - should do nothing. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Enabled() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -469,17 +521,21 @@
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_UpdatedApp() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
+                | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -490,51 +546,60 @@
     public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_UpdatedApp() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        appInfo.flags |= (ApplicationInfo.FLAG_SYSTEM
+                | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP | ApplicationInfo.FLAG_INSTALLED);
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
     /**
-     * Configured app has no privileges, and is in the default state - should disable until use.
-     * Associated app is enabled and should not be touched.
+     * Configured app has no privileges, and is in the default state - should uninstalled.
+     * Associated app is installed and should not be touched.
      */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_EnabledAssociated_Default()
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
         associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         associatedAppInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
-                CALLING_PACKAGE);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
-                USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, false, USER_ID);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                ASSOCIATED_APP, false, USER_ID);
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -542,34 +607,39 @@
 
     /**
      * Configured app has no privileges, and is in the default state along with associated app -
-     * should disable both.
+     * should uninstall both.
      */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Associated_Default()
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
-        associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         associatedAppInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
-                CALLING_PACKAGE);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                ASSOCIATED_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
-                USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, false, USER_ID);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                ASSOCIATED_APP, false, USER_ID);
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
@@ -577,7 +647,7 @@
 
     /**
      * Configured app has no privileges, and is in the default state along with associated app, and
-     * disabling has already occurred - should only disable configured app.
+     * disabling has already occurred - should only uninstall configured app.
      */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Associated_Default_AlreadyRun()
@@ -586,90 +656,108 @@
                 mContentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, USER_ID);
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         ApplicationInfo associatedAppInfo = new ApplicationInfo();
         associatedAppInfo.packageName = ASSOCIATED_APP;
         associatedAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         associatedAppInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(ASSOCIATED_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID))
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, USER_ID))
                 .thenReturn(associatedAppInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
-                CALLING_PACKAGE);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.eq(ASSOCIATED_APP), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                ASSOCIATED_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, false, USER_ID);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.eq(ASSOCIATED_APP), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Telephony is not initialized, and app is in the default state - should disable until use. */
+    /** Telephony is not initialized, and app is in the default state - should uninstall it. */
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Default() throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
-                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
-                CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager).setSystemAppInstallState(
+                CARRIER_APP, false, USER_ID);
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Configured app has no privileges, and is disabled until used - should do nothing. */
+    /** Configured app has no privileges, and is disabled until used or not installed - should do
+     *  nothing.
+     **/
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUntilUsed()
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
                 .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID, CARRIER_APPS, ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
     }
 
-    /** Telephony is not initialized, and app is disabled until used - should do nothing. */
+    /** Telephony is not initialized, and app is disabled until used or not installed - should do
+     *  nothing.
+     **/
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_DisabledUntilUsed()
             throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.packageName = CARRIER_APP;
-        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
         Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+                USER_ID)).thenReturn(appInfo);
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 null /* telephonyManager */, mContentResolver, USER_ID, CARRIER_APPS,
                 ASSOCIATED_APPS);
-        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
-                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
-                Mockito.anyString());
+        Mockito.verify(mPackageManager).setSystemAppHiddenUntilInstalled(
+                CARRIER_APP, true);
+        Mockito.verify(mPackageManager, Mockito.never()).setSystemAppInstallState(
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
         Mockito.verify(mPackageManager, Mockito.never())
                 .grantDefaultPermissionsToEnabledCarrierApps(
                         Mockito.any(String[].class), Mockito.anyInt());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java
deleted file mode 100644
index 6ce6d20..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierIdentifierTest.java
+++ /dev/null
@@ -1,289 +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;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.HandlerThread;
-import android.provider.Telephony.CarrierId;
-import android.provider.Telephony.Carriers;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Arrays;
-
-public class CarrierIdentifierTest extends TelephonyTest {
-    private static final String MCCMNC = "311480";
-    private static final String NAME = "VZW";
-    private static final int CID_VZW = 1;
-
-    private static final String SPN_FI = "PROJECT FI";
-    private static final String NAME_FI = "FI";
-    private static final int CID_FI = 2;
-
-    private static final String NAME_DOCOMO = "DOCOMO";
-    private static final String APN_DOCOMO = "mopera.net";
-    private static final int CID_DOCOMO = 3;
-
-    private static final String NAME_TMO = "TMO";
-    private static final String GID1 = "ae";
-    private static final int CID_TMO = 4;
-
-    private static final int CID_UNKNOWN = -1;
-
-    // events to trigger carrier identification
-    private static final int SIM_LOAD_EVENT       = 1;
-    private static final int SIM_ABSENT_EVENT     = 2;
-    private static final int SPN_OVERRIDE_EVENT   = 3;
-    private static final int PREFER_APN_SET_EVENT = 5;
-
-    private CarrierIdentifier mCarrierIdentifier;
-    private CarrierIdentifierHandler mCarrierIdentifierHandler;
-
-    private class CarrierIdentifierHandler extends HandlerThread {
-        private CarrierIdentifierHandler(String name) {
-            super(name);
-        }
-
-        @Override
-        public void onLooperPrepared() {
-            mCarrierIdentifier = new CarrierIdentifier(mPhone);
-            setReady(true);
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        logd("CarrierIdentifierTest +Setup!");
-        super.setUp(getClass().getSimpleName());
-        ((MockContentResolver) mContext.getContentResolver()).addProvider(
-                CarrierId.AUTHORITY, new CarrierIdContentProvider());
-        // start handler thread
-        mCarrierIdentifierHandler = new CarrierIdentifierHandler(getClass().getSimpleName());
-        mCarrierIdentifierHandler.start();
-        waitUntilReady();
-        logd("CarrierIdentifierTest -Setup!");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        logd("CarrierIdentifier -tearDown");
-        mCarrierIdentifier.removeCallbacksAndMessages(null);
-        mCarrierIdentifier = null;
-        mCarrierIdentifierHandler.quit();
-        super.tearDown();
-    }
-
-    @Test
-    @SmallTest
-    public void testCarrierMatch() {
-        int phoneId = mPhone.getPhoneId();
-        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
-        // trigger sim loading event
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME, mCarrierIdentifier.getCarrierName());
-
-        doReturn(SPN_FI).when(mTelephonyManager).getSimOperatorNameForPhone(eq(phoneId));
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_FI, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME_FI, mCarrierIdentifier.getCarrierName());
-
-        doReturn(GID1).when(mPhone).getGroupIdLevel1();
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_TMO, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME_TMO, mCarrierIdentifier.getCarrierName());
-    }
-
-    @Test
-    @SmallTest
-    public void testCarrierMatchSpnOverride() {
-        int phoneId = mPhone.getPhoneId();
-        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
-        // trigger sim loading event
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME, mCarrierIdentifier.getCarrierName());
-        // spn override
-        doReturn(SPN_FI).when(mTelephonyManager).getSimOperatorNameForPhone(eq(phoneId));
-        mCarrierIdentifier.sendEmptyMessage(SPN_OVERRIDE_EVENT);
-        waitForMs(200);
-        assertEquals(CID_FI, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME_FI, mCarrierIdentifier.getCarrierName());
-    }
-
-    @Test
-    @SmallTest
-    public void testCarrierMatchSimAbsent() {
-        int phoneId = mPhone.getPhoneId();
-        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
-        // trigger sim loading event
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME, mCarrierIdentifier.getCarrierName());
-        // trigger sim absent event
-        mCarrierIdentifier.sendEmptyMessage(SIM_ABSENT_EVENT);
-        waitForMs(200);
-        assertEquals(CID_UNKNOWN, mCarrierIdentifier.getCarrierId());
-        assertNull(mCarrierIdentifier.getCarrierName());
-    }
-
-    @Test
-    @SmallTest
-    public void testCarrierNoMatch() {
-        // un-configured MCCMNC
-        int phoneId = mPhone.getPhoneId();
-        doReturn("12345").when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
-        // trigger sim loading event
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_UNKNOWN, mCarrierIdentifier.getCarrierId());
-        assertNull(mCarrierIdentifier.getCarrierName());
-    }
-
-    @Test
-    @SmallTest
-    public void testCarrierMatchPreferApnChange() {
-        int phoneId = mPhone.getPhoneId();
-        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
-        // trigger sim loading event
-        mCarrierIdentifier.sendEmptyMessage(SIM_LOAD_EVENT);
-        waitForMs(200);
-        assertEquals(CID_VZW, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME, mCarrierIdentifier.getCarrierName());
-        // mock apn
-        ((MockContentResolver) mContext.getContentResolver()).addProvider(
-                Carriers.CONTENT_URI.getAuthority(), new CarrierIdContentProvider());
-        mCarrierIdentifier.sendEmptyMessage(PREFER_APN_SET_EVENT);
-        waitForMs(200);
-        assertEquals(CID_DOCOMO, mCarrierIdentifier.getCarrierId());
-        assertEquals(NAME_DOCOMO, mCarrierIdentifier.getCarrierName());
-    }
-
-    private class CarrierIdContentProvider extends MockContentProvider {
-        @Override
-        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-                String sortOrder) {
-            logd("CarrierIdContentProvider: query");
-            logd("   uri = " + uri);
-            logd("   projection = " + Arrays.toString(projection));
-            logd("   selection = " + selection);
-            logd("   selectionArgs = " + Arrays.toString(selectionArgs));
-            logd("   sortOrder = " + sortOrder);
-
-            if (CarrierId.All.CONTENT_URI.getAuthority().equals(
-                    uri.getAuthority())) {
-                MatrixCursor mc = new MatrixCursor(
-                        new String[]{CarrierId._ID,
-                                CarrierId.All.MCCMNC,
-                                CarrierId.All.GID1,
-                                CarrierId.All.GID2,
-                                CarrierId.All.PLMN,
-                                CarrierId.All.IMSI_PREFIX_XPATTERN,
-                                CarrierId.All.ICCID_PREFIX,
-                                CarrierId.All.SPN,
-                                CarrierId.All.APN,
-                                CarrierId.CARRIER_NAME,
-                                CarrierId.CARRIER_ID});
-
-                mc.addRow(new Object[] {
-                        1,                      // id
-                        MCCMNC,                 // mccmnc
-                        null,                   // gid1
-                        null,                   // gid2
-                        null,                   // plmn
-                        null,                   // imsi_prefix
-                        null,                   // iccid_prefix
-                        null,                   // spn
-                        null,                   // apn
-                        NAME,                   // carrier name
-                        CID_VZW,                // cid
-                });
-                mc.addRow(new Object[] {
-                        2,                      // id
-                        MCCMNC,                 // mccmnc
-                        GID1,                   // gid1
-                        null,                   // gid2
-                        null,                   // plmn
-                        null,                   // imsi_prefix
-                        null,                   // iccid_prefix
-                        null,                   // spn
-                        null,                   // apn
-                        NAME_TMO,               // carrier name
-                        CID_TMO,                // cid
-                });
-                mc.addRow(new Object[] {
-                        3,                      // id
-                        MCCMNC,                 // mccmnc
-                        null,                   // gid1
-                        null,                   // gid2
-                        null,                   // plmn
-                        null,                   // imsi_prefix
-                        null,                   // iccid_prefix
-                        SPN_FI,                 // spn
-                        null,                   // apn
-                        NAME_FI,                // carrier name
-                        CID_FI,                 // cid
-                });
-                mc.addRow(new Object[] {
-                        4,                      // id
-                        MCCMNC,                 // mccmnc
-                        null,                   // gid1
-                        null,                   // gid2
-                        null,                   // plmn
-                        null,                   // imsi_prefix
-                        null,                   // iccid_prefix
-                        null,                   // spn
-                        APN_DOCOMO,             // apn
-                        NAME_DOCOMO,            // carrier name
-                        CID_DOCOMO,             // cid
-                });
-                return mc;
-            } else if (Carriers.CONTENT_URI.getAuthority().equals(uri.getAuthority())) {
-                MatrixCursor mc = new MatrixCursor(new String[]{Carriers._ID, Carriers.APN});
-                mc.addRow(new Object[] {
-                        1,                      // id
-                        APN_DOCOMO              // apn
-                });
-                return mc;
-            }
-            return null;
-        }
-        @Override
-        public int update(android.net.Uri uri, android.content.ContentValues values,
-                java.lang.String selection, java.lang.String[] selectionArgs) {
-            return 0;
-        }
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
new file mode 100644
index 0000000..6a57469
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
@@ -0,0 +1,522 @@
+/*
+ * 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;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.provider.Telephony.CarrierId;
+import android.provider.Telephony.Carriers;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class CarrierResolverTest extends TelephonyTest {
+    private static final String MCCMNC = "311480";
+    private static final String NAME = "VZW";
+    private static final int CID_VZW = 1;
+
+    private static final String MCCMNC_VODAFONE = "20205";
+    private static final String NAME_VODAFONE = "VODAFONE";
+    private static final String SPN_VODAFONE = "vodafone GR";
+    private static final int CID_VODAFONE = 5;
+
+    private static final String SPN_FI = "PROJECT FI";
+    private static final String NAME_FI = "FI";
+    private static final int CID_FI = 2;
+
+    private static final String NAME_DOCOMO = "DOCOMO";
+    private static final String APN_DOCOMO = "mopera.net";
+    private static final int CID_DOCOMO = 3;
+
+    private static final String NAME_TMO = "TMO";
+    private static final String MCCMNC_TMO = "310260";
+    private static final int CID_TMO = 4;
+
+    private static final String MCCMNC_ATT = "310410";
+    private static final int CID_ATT = 5;
+
+    private static final int CID_TRACFONE = 6;
+    private static final int CID_TRACFONE_ATT = 7;
+    private static final int CID_TRACFONE_TMO = 8;
+    private static final String MCCMNC_TRACFONE_ATT = "310410";
+    private static final String MCCMNC_TRACFONE_TMO = "310260";
+    private static final String GID_TRACFONE = "DDFF";
+
+    private static final int CID_O2 = 9;
+    private static final int CID_O2_PREPAID = 10;
+    private static final String MCCMNC_O2 = "23410";
+    private static final String GID_O2_PREPAID = "61";
+
+
+    private static final int CID_UNKNOWN = -1;
+
+    // events to trigger carrier identification
+    private static final int SIM_LOAD_EVENT       = 1;
+    private static final int ICC_CHANGED_EVENT    = 2;
+    private static final int PREFER_APN_SET_EVENT = 3;
+
+    private CarrierResolver mCarrierResolver;
+    private CarrierResolverHandler mCarrierCarrierResolverHandler;
+
+    private class CarrierResolverHandler extends HandlerThread {
+        private CarrierResolverHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mCarrierResolver = new CarrierResolver(mPhone);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("CarrierResolverTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        ((MockContentResolver) mContext.getContentResolver()).addProvider(
+                CarrierId.AUTHORITY, new CarrierIdContentProvider());
+        // start handler thread
+        mCarrierCarrierResolverHandler = new CarrierResolverHandler(getClass().getSimpleName());
+        mCarrierCarrierResolverHandler.start();
+        waitUntilReady();
+        mCarrierResolver.sendEmptyMessage(ICC_CHANGED_EVENT);
+        logd("CarrierResolverTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        logd("CarrierResolver -tearDown");
+        mCarrierResolver.removeCallbacksAndMessages(null);
+        mCarrierResolver = null;
+        mCarrierCarrierResolverHandler.quit();
+        mCarrierCarrierResolverHandler.join();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierMatch() {
+        int phoneId = mPhone.getPhoneId();
+        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+        // trigger sim loading event
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_VZW, mCarrierResolver.getCarrierId());
+        assertEquals(NAME, mCarrierResolver.getCarrierName());
+
+        doReturn(SPN_FI).when(mSimRecords).getServiceProviderName();
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_FI, mCarrierResolver.getCarrierId());
+        assertEquals(NAME_FI, mCarrierResolver.getCarrierName());
+    }
+
+    @Test
+    @SmallTest
+    public void testMnoCarrierId() {
+        int phoneId = mPhone.getPhoneId();
+        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+        doReturn(SPN_FI).when(mSimRecords).getServiceProviderName();
+
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+
+        assertEquals(CID_FI, mCarrierResolver.getCarrierId());
+        assertEquals(NAME_FI, mCarrierResolver.getCarrierName());
+        assertEquals(CID_VZW, mCarrierResolver.getMnoCarrierId());
+
+        doReturn(MCCMNC_VODAFONE).when(mTelephonyManager)
+                .getSimOperatorNumericForPhone(eq(phoneId));
+        doReturn(SPN_VODAFONE).when(mSimRecords).getServiceProviderName();
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_VODAFONE, mCarrierResolver.getCarrierId());
+        assertEquals(NAME_VODAFONE, mCarrierResolver.getCarrierName());
+        assertEquals(CID_VODAFONE, mCarrierResolver.getMnoCarrierId());
+    }
+
+    @Test
+    @SmallTest
+    public void testPreciseCarrierId() {
+        int phoneId = mPhone.getPhoneId();
+        doReturn(MCCMNC_TRACFONE_ATT).when(mTelephonyManager)
+                .getSimOperatorNumericForPhone(eq(phoneId));
+
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_ATT, mCarrierResolver.getCarrierId());
+        assertEquals(CID_ATT, mCarrierResolver.getPreciseCarrierId());
+
+        doReturn(GID_TRACFONE).when(mPhone).getGroupIdLevel1();
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_TRACFONE, mCarrierResolver.getCarrierId());
+        assertEquals(CID_TRACFONE_ATT, mCarrierResolver.getPreciseCarrierId());
+
+        doReturn(MCCMNC_TRACFONE_TMO).when(mTelephonyManager)
+                .getSimOperatorNumericForPhone(eq(phoneId));
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_TRACFONE, mCarrierResolver.getCarrierId());
+        assertEquals(CID_TRACFONE_TMO, mCarrierResolver.getPreciseCarrierId());
+
+        doReturn(MCCMNC_O2).when(mTelephonyManager)
+                .getSimOperatorNumericForPhone(eq(phoneId));
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_O2, mCarrierResolver.getCarrierId());
+        assertEquals(CID_O2, mCarrierResolver.getPreciseCarrierId());
+
+        doReturn(MCCMNC_O2).when(mTelephonyManager)
+                .getSimOperatorNumericForPhone(eq(phoneId));
+        doReturn(GID_O2_PREPAID).when(mPhone).getGroupIdLevel1();
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_O2, mCarrierResolver.getCarrierId());
+        assertEquals(CID_O2_PREPAID, mCarrierResolver.getPreciseCarrierId());
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierMatchSimAbsent() {
+        int phoneId = mPhone.getPhoneId();
+        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+        // trigger sim loading event
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_VZW, mCarrierResolver.getCarrierId());
+        assertEquals(NAME, mCarrierResolver.getCarrierName());
+        // trigger sim absent event
+        mCarrierResolver.resolveSubscriptionCarrierId(IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        waitForMs(200);
+        assertEquals(CID_UNKNOWN, mCarrierResolver.getCarrierId());
+        assertNull(mCarrierResolver.getCarrierName());
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierNoMatch() {
+        // un-configured MCCMNC
+        int phoneId = mPhone.getPhoneId();
+        doReturn("12345").when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+        // trigger sim loading event
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_UNKNOWN, mCarrierResolver.getCarrierId());
+        assertNull(mCarrierResolver.getCarrierName());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetCarrierIdFromIdentifier() {
+        // trigger sim loading event
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+
+        CarrierIdentifier identifier = new CarrierIdentifier(null, null, null, null, null, null);
+        int carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
+        assertEquals(CID_UNKNOWN, carrierid);
+
+        identifier = new CarrierIdentifier(MCCMNC.substring(0, 3), MCCMNC.substring(3), null, null,
+                null, null);
+        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
+        assertEquals(CID_VZW, carrierid);
+
+        identifier = new CarrierIdentifier(MCCMNC.substring(0, 3), MCCMNC.substring(3),  SPN_FI, null,
+                null, null);
+        carrierid = mCarrierResolver.getCarrierIdFromIdentifier(mContext, identifier);
+        assertEquals(CID_FI, carrierid);
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierMatchPreferApnChange() {
+        int phoneId = mPhone.getPhoneId();
+        doReturn(MCCMNC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(phoneId));
+        // trigger sim loading event
+        mCarrierResolver.sendEmptyMessage(SIM_LOAD_EVENT);
+        waitForMs(200);
+        assertEquals(CID_VZW, mCarrierResolver.getCarrierId());
+        assertEquals(NAME, mCarrierResolver.getCarrierName());
+        // mock apn
+        ((MockContentResolver) mContext.getContentResolver()).addProvider(
+                Carriers.CONTENT_URI.getAuthority(), new CarrierIdContentProvider());
+        mCarrierResolver.sendEmptyMessage(PREFER_APN_SET_EVENT);
+        waitForMs(200);
+        assertEquals(CID_DOCOMO, mCarrierResolver.getCarrierId());
+        assertEquals(NAME_DOCOMO, mCarrierResolver.getCarrierName());
+    }
+
+    private class CarrierIdContentProvider extends MockContentProvider {
+        @Override
+        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+                String sortOrder) {
+            logd("CarrierIdContentProvider: query");
+            logd("   uri = " + uri);
+            logd("   projection = " + Arrays.toString(projection));
+            logd("   selection = " + selection);
+            logd("   selectionArgs = " + Arrays.toString(selectionArgs));
+            logd("   sortOrder = " + sortOrder);
+
+            if (CarrierId.All.CONTENT_URI.getAuthority().equals(
+                    uri.getAuthority())) {
+                MatrixCursor mc = new MatrixCursor(
+                        new String[]{CarrierId._ID,
+                                CarrierId.All.MCCMNC,
+                                CarrierId.All.GID1,
+                                CarrierId.All.GID2,
+                                CarrierId.All.PLMN,
+                                CarrierId.All.IMSI_PREFIX_XPATTERN,
+                                CarrierId.All.ICCID_PREFIX,
+                                CarrierId.All.PRIVILEGE_ACCESS_RULE,
+                                CarrierId.All.SPN,
+                                CarrierId.All.APN,
+                                CarrierId.CARRIER_NAME,
+                                CarrierId.CARRIER_ID,
+                                CarrierId.PARENT_CARRIER_ID});
+
+                mc.addRow(new Object[] {
+                        1,                      // id
+                        MCCMNC,                 // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access rule
+                        null,                   // spn
+                        null,                   // apn
+                        NAME,                   // carrier name
+                        CID_VZW,                // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        2,                      // id
+                        MCCMNC_TMO,             // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        NAME_TMO,               // carrier name
+                        CID_TMO,                // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        3,                      // id
+                        MCCMNC,                 // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        SPN_FI,                 // spn
+                        null,                   // apn
+                        NAME_FI,                // carrier name
+                        CID_FI,                 // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        4,                      // id
+                        MCCMNC,                 // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        SPN_FI,                 // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_FI,                 // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        5,                      // id
+                        MCCMNC,                 // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        APN_DOCOMO,             // apn
+                        NAME_DOCOMO,            // carrier name
+                        CID_DOCOMO,             // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        6,                      // id
+                        MCCMNC_VODAFONE,        // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        SPN_VODAFONE,           // spn
+                        null,                   // apn
+                        NAME_VODAFONE,          // carrier name
+                        CID_VODAFONE,           // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        7,                      // id
+                        MCCMNC_ATT,             // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_ATT,                // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        8,                      // id
+                        MCCMNC_TRACFONE_ATT,    // mccmnc
+                        GID_TRACFONE,           // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_TRACFONE,           // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        9,                      // id
+                        MCCMNC_TRACFONE_TMO,    // mccmnc
+                        GID_TRACFONE,           // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_TRACFONE,           // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID, // parent cid
+                });
+                mc.addRow(new Object[] {
+                        10,                     // id
+                        MCCMNC_TRACFONE_ATT,    // mccmnc
+                        GID_TRACFONE,           // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_TRACFONE_ATT,       // cid
+                        CID_TRACFONE,           // parent cid
+                });
+                mc.addRow(new Object[] {
+                        11,                     // id
+                        MCCMNC_TRACFONE_TMO,    // mccmnc
+                        GID_TRACFONE,           // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_TRACFONE_TMO,       // cid
+                        CID_TRACFONE,           // parent cid
+                });
+                mc.addRow(new Object[] {
+                        12,                     // id
+                        MCCMNC_O2,              // mccmnc
+                        null,                   // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_O2,                 // cid
+                        TelephonyManager.UNKNOWN_CARRIER_ID,  // parent cid
+                });
+                mc.addRow(new Object[] {
+                        13,                     // id
+                        MCCMNC_O2,              // mccmnc
+                        GID_O2_PREPAID,         // gid1
+                        null,                   // gid2
+                        null,                   // plmn
+                        null,                   // imsi_prefix
+                        null,                   // iccid_prefix
+                        null,                   // access_rule
+                        null,                   // spn
+                        null,                   // apn
+                        null,                   // carrier name
+                        CID_O2_PREPAID,         // cid
+                        CID_O2,                 // parent cid
+                });
+                return mc;
+            } else if (Carriers.CONTENT_URI.getAuthority().equals(uri.getAuthority())) {
+                MatrixCursor mc = new MatrixCursor(new String[]{Carriers._ID, Carriers.APN});
+                mc.addRow(new Object[] {
+                        1,                      // id
+                        APN_DOCOMO              // apn
+                });
+                return mc;
+            }
+            return null;
+        }
+        @Override
+        public int update(android.net.Uri uri, android.content.ContentValues values,
+                java.lang.String selection, java.lang.String[] selectionArgs) {
+            return 0;
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 358fa61..2dc5c66 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -168,7 +168,7 @@
         mSpyCarrierSST.getContentObserver().dispatchChange(false,
                 Settings.Global.getUriFor(prefNetworkMode));
         waitForMs(500);
-        verify(mNotificationManager).notify(
+        verify(mNotificationManager, atLeast(1)).notify(
                 eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
 
         Settings.Global.putInt(mContext.getContentResolver(), prefNetworkMode,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
index 6635c08..0a14b65 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -31,6 +32,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.service.carrier.CarrierMessagingService;
 import android.service.carrier.ICarrierMessagingCallback;
@@ -71,9 +73,14 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+            Looper.loop();
+        }
         mCarrierServicesSmsFilterUT = new CarrierServicesSmsFilter(
                 mContext, mPhone, new byte[][]{SMS_PDU},
-                0, "3gpp", mFilterCallback, getClass().getSimpleName());
+                0, "3gpp", mFilterCallback, getClass().getSimpleName()
+        );
     }
 
     @After
@@ -92,7 +99,8 @@
     public void testFilter_carrierAppPresent_handled() throws Exception {
         mockCarrierApp();
         mockCarrierAppStubResults(
-                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mICarrierAppMessagingService);
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mICarrierAppMessagingService,
+                true);
         assertTrue(mCarrierServicesSmsFilterUT.filter());
 
         verify(mFilterCallback, timeout(100))
@@ -104,7 +112,8 @@
     public void testFilter_systemAppPresent_handled() throws Exception {
         mockSystemApp();
         mockCarrierAppStubResults(
-                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService);
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService,
+                true);
 
         assertTrue(mCarrierServicesSmsFilterUT.filter());
 
@@ -118,9 +127,11 @@
         mockCarrierApp();
         mockSystemApp();
         mockCarrierAppStubResults(
-                CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT, mICarrierAppMessagingService);
+                CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT, mICarrierAppMessagingService,
+                true);
         mockCarrierAppStubResults(
-                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService);
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService,
+                true);
 
         assertTrue(mCarrierServicesSmsFilterUT.filter());
 
@@ -128,6 +139,34 @@
                 .onFilterComplete(eq(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT));
     }
 
+    @Test
+    public void testFilterSmsShouldNotTimeout_whenOnFilterCompleteCalled() throws Exception {
+        //This will make sure mCarrierServicesSmsFilterUT.filter() will return true, and therefore
+        // filterSms() will return true
+        mockCarrierApp();
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT, mICarrierAppMessagingService,
+                true);
+        assertTrue(mCarrierServicesSmsFilterUT.filter());
+
+        verify(mFilterCallback, times(1))
+                .onFilterComplete(eq(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT));
+    }
+
+    @Test
+    public void testFilterSmsShouldTimeout_whenOnFilterCompleteNotCalled() throws Exception {
+        //This will make sure mCarrierServicesSmsFilterUT.filter() will return true, and therefore
+        // filterSms() will return true
+        mockCarrierApp();
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT, mICarrierAppMessagingService,
+                false);
+        assertTrue(mCarrierServicesSmsFilterUT.filter());
+
+        verify(mFilterCallback, times(0))
+                .onFilterComplete(anyInt());
+    }
+
     private void mockCarrierApp()
             throws RemoteException {
         mContextFixture.addService(
@@ -158,7 +197,8 @@
                 serviceInfo);
     }
 
-    private void mockCarrierAppStubResults(final int result, ICarrierMessagingService.Stub stub)
+    private void mockCarrierAppStubResults(final int result, ICarrierMessagingService.Stub stub,
+            boolean callOnFilterComplete)
             throws RemoteException {
         when(stub.queryLocalInterface(anyString())).thenReturn(stub);
         when(stub.asBinder()).thenReturn(stub);
@@ -167,7 +207,9 @@
             public Void answer(InvocationOnMock invocation) throws Throwable {
                 Object[] args = invocation.getArguments();
                 ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
-                callback.onFilterComplete(result);
+                if (callOnFilterComplete) {
+                    callback.onFilterComplete(result);
+                }
                 return null;
             }
         }).when(stub).filterSms(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java
new file mode 100644
index 0000000..dc2c320
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellInfo;
+import android.test.AndroidTestCase;
+
+import org.junit.Test;
+
+public class CellIdentityNrTest extends AndroidTestCase {
+    private static final String MCC = "310";
+    private static final String MNC = "260";
+    private static final String ANOTHER_MCC = "134";
+    private static final String ANOTHER_MNC = "256";
+    private static final String ALPHAL = "long operator name";
+    private static final String ALPHAS = "lon";
+    private static final int NRARFCN = 13456;
+    private static final int PCI = 123;
+    private static final int TAC = 32767;
+
+    @Test
+    public void testGetMethod() {
+        // GIVEN an instance of CellIdentityNr
+        CellIdentityNr cellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, MCC, MNC, ALPHAL, ALPHAS);
+
+        // THEN the get method should return correct value
+        assertThat(cellIdentityNr.getType()).isEqualTo(CellInfo.TYPE_NR);
+        assertThat(cellIdentityNr.getChannelNumber()).isEqualTo(NRARFCN);
+        assertThat(cellIdentityNr.getPci()).isEqualTo(PCI);
+        assertThat(cellIdentityNr.getTac()).isEqualTo(TAC);
+        assertThat(cellIdentityNr.getOperatorAlphaLong()).isEqualTo(ALPHAL);
+        assertThat(cellIdentityNr.getOperatorAlphaShort()).isEqualTo(ALPHAS);
+        assertThat(cellIdentityNr.getMccString()).isEqualTo(MCC);
+        assertThat(cellIdentityNr.getMncString()).isEqualTo(MNC);
+    }
+
+    @Test
+    public void testEquals_sameParameters() {
+        // GIVEN an instance of CellIdentityNr, and create another object with the same parameters
+        CellIdentityNr cellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, MCC, MNC, ALPHAL, ALPHAS);
+        CellIdentityNr anotherCellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, MCC, MNC, ALPHAL, ALPHAS);
+
+        // THEN this two objects are equivalent
+        assertThat(cellIdentityNr).isEqualTo(anotherCellIdentityNr);
+    }
+
+    @Test
+    public void testEquals_differentParameters() {
+        // GIVEN an instance of CellIdentityNr, and create another object with different parameters
+        CellIdentityNr cellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, MCC, MNC, ALPHAL, ALPHAS);
+        CellIdentityNr anotherCellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, ANOTHER_MCC, ANOTHER_MNC, ALPHAL, ALPHAS);
+
+        // THEN this two objects are different
+        assertThat(cellIdentityNr).isNotEqualTo(anotherCellIdentityNr);
+    }
+
+    @Test
+    public void testParcel() {
+        // GIVEN an instance of CellIdentityNr
+        CellIdentityNr cellIdentityNr =
+                new CellIdentityNr(PCI, TAC, NRARFCN, MCC, MNC, ALPHAL, ALPHAS);
+
+        // WHEN write the object to parcel and create another object with that parcel
+        Parcel parcel = Parcel.obtain();
+        cellIdentityNr.writeToParcel(parcel, 0 /* type */);
+        parcel.setDataPosition(0);
+        CellIdentityNr anotherCellIdentityNr = CellIdentityNr.CREATOR.createFromParcel(parcel);
+
+        // THEN the new object is equal to the old one
+        assertThat(anotherCellIdentityNr).isEqualTo(anotherCellIdentityNr);
+        assertThat(anotherCellIdentityNr.getType()).isEqualTo(CellInfo.TYPE_NR);
+        assertThat(anotherCellIdentityNr.getChannelNumber()).isEqualTo(NRARFCN);
+        assertThat(anotherCellIdentityNr.getPci()).isEqualTo(PCI);
+        assertThat(anotherCellIdentityNr.getTac()).isEqualTo(TAC);
+        assertThat(anotherCellIdentityNr.getOperatorAlphaLong()).isEqualTo(ALPHAL);
+        assertThat(anotherCellIdentityNr.getOperatorAlphaShort()).isEqualTo(ALPHAS);
+        assertThat(anotherCellIdentityNr.getMccString()).isEqualTo(MCC);
+        assertThat(anotherCellIdentityNr.getMncString()).isEqualTo(MNC);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
new file mode 100644
index 0000000..2fdf0b1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthNr;
+import android.test.AndroidTestCase;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.Range;
+
+import org.junit.Test;
+
+public class CellSignalStrengthNrTest extends AndroidTestCase {
+    private static final int CSIRSRP = -123;
+    private static final int CSIRSRQ = -111;
+    private static final int ANOTHER_CSIRSRP = -111;
+    private static final int ANOTHER_CSIRSRQ = -120;
+    private static final int INVALID_CSIRSRP = Integer.MAX_VALUE;
+    private static final int CSISINR = 64;
+    private static final int SSRSRP = -112;
+    private static final int SSRSRQ = -94;
+    private static final int SSSINR = 32;
+
+    @Test
+    public void testGetMethod() {
+        // GIVEN an instance of CellSignalStrengthNr
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN the get method should return correct value
+        assertThat(css.getCsiRsrp()).isEqualTo(CSIRSRP);
+        assertThat(css.getCsiRsrq()).isEqualTo(CSIRSRQ);
+        assertThat(css.getCsiSinr()).isEqualTo(CSISINR);
+        assertThat(css.getSsRsrp()).isEqualTo(SSRSRP);
+        assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
+        assertThat(css.getSsSinr()).isEqualTo(SSSINR);
+    }
+
+    @Test
+    public void testEquals_sameParameters() {
+        // GIVEN an instance of CellSignalStrengthNr and another object with the same parameters
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+        CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN this two objects are equivalent
+        assertThat(css).isEqualTo(anotherCss);
+    }
+
+    @Test
+    public void testEquals_differentParameters() {
+        // GIVEN an instance of CellSignalStrengthNr and another object with some different
+        // parameters
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+        CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(
+                ANOTHER_CSIRSRP, ANOTHER_CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN this two objects are different
+        assertThat(css).isNotEqualTo(anotherCss);
+    }
+
+    @Test
+    public void testAusLevel_validValue() {
+        // GIVEN an instance of CellSignalStrengthNr with valid csirsrp
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN the asu level is in range [0, 97]
+        assertThat(css.getAsuLevel()).isIn(Range.range(0, BoundType.CLOSED, 97, BoundType.CLOSED));
+    }
+
+    @Test
+    public void testAsuLevel_invalidValue() {
+        // GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                INVALID_CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN the asu level is unknown
+        assertThat(css.getAsuLevel()).isEqualTo(CellSignalStrengthNr.UNKNOWN_ASU_LEVEL);
+    }
+
+    @Test
+    public void testSignalLevel_validValue() {
+        for (int csiRsrp = -140; csiRsrp <= -44; csiRsrp++) {
+            // GIVEN an instance of CellSignalStrengthNr with valid csirsrp
+            CellSignalStrengthNr css = new CellSignalStrengthNr(
+                    csiRsrp, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+            // THEN the signal level is valid
+            assertThat(css.getLevel()).isAnyOf(
+                    CellSignalStrength.SIGNAL_STRENGTH_GREAT,
+                    CellSignalStrength.SIGNAL_STRENGTH_GOOD,
+                    CellSignalStrength.SIGNAL_STRENGTH_MODERATE,
+                    CellSignalStrength.SIGNAL_STRENGTH_POOR);
+        }
+    }
+
+    @Test
+    public void testSignalLevel_invalidValue() {
+        // GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                INVALID_CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // THEN the signal level is unknown
+        assertThat(css.getLevel()).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+    }
+
+    @Test
+    public void testParcel() {
+        // GIVEN an instance of CellSignalStrengthNr
+        CellSignalStrengthNr css = new CellSignalStrengthNr(
+                CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+
+        // WHEN write the object to parcel and create another object with that parcel
+        Parcel parcel = Parcel.obtain();
+        css.writeToParcel(parcel, 0 /* type */);
+        parcel.setDataPosition(0);
+        CellSignalStrengthNr anotherCss = CellSignalStrengthNr.CREATOR.createFromParcel(parcel);
+
+        // THEN the new object is equal to the old one
+        assertThat(anotherCss).isEqualTo(css);
+        assertThat(anotherCss.getCsiRsrp()).isEqualTo(CSIRSRP);
+        assertThat(anotherCss.getCsiRsrq()).isEqualTo(CSIRSRQ);
+        assertThat(anotherCss.getCsiSinr()).isEqualTo(CSISINR);
+        assertThat(anotherCss.getSsRsrp()).isEqualTo(SSRSRP);
+        assertThat(anotherCss.getSsRsrq()).isEqualTo(SSRSRQ);
+        assertThat(anotherCss.getSsSinr()).isEqualTo(SSSINR);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
index a595c36..b2ee494 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -157,7 +157,7 @@
         expectedState = new NetworkRegistrationState(
                 domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
                 ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
-                false, availableServices, null, maxDataCalls);
+                false, availableServices, null, maxDataCalls, false, false);
 
         try {
             verify(mCallback, times(1)).onGetNetworkRegistrationStateComplete(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index d7e4d4e..1785c8f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -30,7 +30,6 @@
 import android.telephony.PreciseDisconnectCause;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
-import android.telephony.VoLteServiceState;
 import android.telephony.gsm.GsmCellLocation;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -271,15 +270,4 @@
         mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, TelephonyManager.OTASP_UNKNOWN);
         verify(mTelephonyRegisteryMock).notifyOtaspChanged(TelephonyManager.OTASP_UNKNOWN);
     }
-
-    @Test @SmallTest
-    public void testNotifyVoLteServiceStateChanged() throws Exception {
-        VoLteServiceState state = new VoLteServiceState(VoLteServiceState.NOT_SUPPORTED);
-        mDefaultPhoneNotifierUT.notifyVoLteServiceStateChanged(mPhone, state);
-        verify(mTelephonyRegisteryMock).notifyVoLteServiceStateChanged(state);
-
-        state = new VoLteServiceState(VoLteServiceState.HANDOVER_COMPLETED);
-        mDefaultPhoneNotifierUT.notifyVoLteServiceStateChanged(mPhone, state);
-        verify(mTelephonyRegisteryMock).notifyVoLteServiceStateChanged(state);
-    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index 591b111..1f883b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -124,4 +124,12 @@
         verify(mSimulatedCommandsVerifier, times(1)).sendDeviceState(eq(CHARGING_STATE),
                 eq(false), nullable(Message.class));
     }
+
+    @FlakyTest
+    public void testReset() throws Exception {
+        mDSM.obtainMessage(6).sendToTarget();
+
+        verify(mSimulatedCommandsVerifier, times(1)).setUnsolResponseFilter(eq(-1),
+                nullable(Message.class));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index b794b4d..2271a5a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -97,7 +97,9 @@
                     + SubscriptionManager.WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1,"
                     + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1,"
                     + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0,"
-                    + SubscriptionManager.PARENT_SUB_ID + " INTEGER DEFAULT -1"
+                    + SubscriptionManager.GROUP_UUID + " TEXT,"
+                    + SubscriptionManager.IS_METERED + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.ISO_COUNTRY_CODE + " TEXT"
                     + ");";
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index ea67595..d12b6cc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -452,5 +453,23 @@
         // verify that call radio tech is set
         verify(mConnection).setCallRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
     }
+
+    @Test
+    @SmallTest
+    public void testCantCallOtaspInProgress() {
+        mDialString = "*22899";
+        testMOCallDial();
+        waitForHandlerAction(mSimulatedCommands.getHandler(), 5000);
+        mSimulatedCommands.progressConnectingToActive();
+        waitForHandlerAction(mSimulatedCommands.getHandler(), 5000);
+        // Try to place another call.
+        try {
+            mCTUT.dial("650-555-1212");
+        } catch (CallStateException cse) {
+            assertEquals(CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS, cse.getError());
+            return;
+        }
+        fail("Expected otasp call state exception");
+    }
 }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index f4a3311..ce2d62f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -61,7 +61,7 @@
 
         @Override
         public void onLooperPrepared() {
-            mLocaleTracker = new LocaleTracker(mPhone, this.getLooper());
+            mLocaleTracker = new LocaleTracker(mPhone, mNitzStateMachine, this.getLooper());
             setReady(true);
         }
     }
@@ -89,7 +89,7 @@
             Message m = invocation.getArgument(1);
             AsyncResult.forMessage(m, Arrays.asList(mCellInfo), null);
             m.sendToTarget();
-            return null; }).when(mPhone).getAllCellInfo(any(), any());
+            return null; }).when(mPhone).requestCellInfoUpdate(any(), any());
 
         logd("LocaleTrackerTest -Setup!");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index de98c67..ca398bc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -19,12 +19,17 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.Ignore;
+
 import java.util.Locale;
 
+// TODO try using InstrumentationRegistry.getContext() instead of the default
+// AndroidTestCase context
 public class MccTableTest extends AndroidTestCase {
     private final static String LOG_TAG = "GSM";
 
     @SmallTest
+    @Ignore
     public void testTimeZone() throws Exception {
         assertEquals("Europe/Paris", MccTable.defaultTimeZoneForMcc(208));
         assertEquals("Europe/Vienna", MccTable.defaultTimeZoneForMcc(232));
@@ -41,6 +46,7 @@
     }
 
     @SmallTest
+    @Ignore
     public void testCountryCode() throws Exception {
         assertEquals("lu", MccTable.countryCodeForMcc(270));
         assertEquals("gr", MccTable.countryCodeForMcc(202));
@@ -53,6 +59,7 @@
     }
 
     @SmallTest
+    @Ignore
     public void testLang() throws Exception {
         assertEquals("en", MccTable.defaultLanguageForMcc(311));
         assertEquals("de", MccTable.defaultLanguageForMcc(232));
@@ -64,6 +71,7 @@
     }
 
     @SmallTest
+    @Ignore
     public void testLang_India() throws Exception {
         assertEquals("en", MccTable.defaultLanguageForMcc(404));
         assertEquals("en", MccTable.defaultLanguageForMcc(405));
@@ -71,6 +79,7 @@
     }
 
     @SmallTest
+    @Ignore
     public void testLocale() throws Exception {
         assertEquals(Locale.forLanguageTag("en-CA"),
                 MccTable.getLocaleFromMcc(getContext(), 302, null));
@@ -87,6 +96,7 @@
     }
 
     @SmallTest
+    @Ignore
     public void testSmDigits() throws Exception {
         assertEquals(3, MccTable.smallestDigitsMccForMnc(312));
         assertEquals(2, MccTable.smallestDigitsMccForMnc(430));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
index 47a9979..041c5f5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
@@ -45,7 +45,6 @@
         CellSignalStrengthGsm cssg = new CellSignalStrengthGsm(5, 6, 7);
         CellInfoGsm gsm = new CellInfoGsm();
         gsm.setRegistered(true);
-        gsm.setTimeStampType(8);
         gsm.setTimeStamp(9);
         gsm.setCellIdentity(cig);
         gsm.setCellSignalStrength(cssg);
@@ -55,7 +54,6 @@
         CellSignalStrengthLte cssl = new CellSignalStrengthLte(15, 16, 17, 18, 19, 20);
         CellInfoLte lte = new CellInfoLte();
         lte.setRegistered(false);
-        lte.setTimeStampType(21);
         lte.setTimeStamp(22);
         lte.setCellIdentity(cil);
         lte.setCellSignalStrength(cssl);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerExecutorTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerExecutorTest.java
new file mode 100644
index 0000000..40940bd
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerExecutorTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class PhoneStateListenerExecutorTest extends TelephonyTest {
+
+    private Executor mSimpleExecutor = new Executor() {
+        @Override
+        public void execute(Runnable r) {
+            r.run();
+        }
+    };
+
+    private PhoneStateListener mPhoneStateListenerUT;
+
+    private boolean mUserMobileDataState = false;
+    private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
+    @Before
+    public void setUp() throws Exception {
+        this.setUp(this.getClass().getSimpleName());
+
+        mPhoneStateListenerUT = new PhoneStateListener(mSimpleExecutor) {
+            @Override
+            public void onServiceStateChanged(ServiceState serviceState) {
+                logd("Service State Changed");
+                mServiceState.setVoiceRegState(serviceState.getVoiceRegState());
+                mServiceState.setDataRegState(serviceState.getDataRegState());
+            }
+
+            @Override
+            public void onUserMobileDataStateChanged(boolean state) {
+                logd("User Mobile Data State Changed");
+                mUserMobileDataState = true;
+            }
+
+            @Override
+            public void onPhysicalChannelConfigurationChanged(
+                    List<PhysicalChannelConfig> configs) {
+                logd("PhysicalChannelConfig Changed");
+                mPhysicalChannelConfigs = configs;
+            }
+        };
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test @SmallTest
+    public void testTriggerServiceStateChanged() throws Exception {
+        Field field = PhoneStateListener.class.getDeclaredField("callback");
+        field.setAccessible(true);
+
+        ServiceState ss = new ServiceState();
+        ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+        ss.setVoiceRegState(ServiceState.STATE_EMERGENCY_ONLY);
+
+        ((IPhoneStateListener) field.get(mPhoneStateListenerUT)).onServiceStateChanged(ss);
+
+        verify(mServiceState).setDataRegState(ServiceState.STATE_IN_SERVICE);
+        verify(mServiceState).setVoiceRegState(ServiceState.STATE_EMERGENCY_ONLY);
+    }
+
+    @Test @SmallTest
+    public void testTriggerUserMobileDataStateChanged() throws Exception {
+        Field field = PhoneStateListener.class.getDeclaredField("callback");
+        field.setAccessible(true);
+
+        assertFalse(mUserMobileDataState);
+
+        ((IPhoneStateListener) field.get(mPhoneStateListenerUT)).onUserMobileDataStateChanged(true);
+
+        assertTrue(mUserMobileDataState);
+    }
+
+    @Test @SmallTest
+    public void testTriggerPhysicalChannelConfigurationChanged() throws Exception {
+        Field field = PhoneStateListener.class.getDeclaredField("callback");
+        field.setAccessible(true);
+
+        assertNull(mPhysicalChannelConfigs);
+
+        PhysicalChannelConfig config = new PhysicalChannelConfig.Builder()
+                .setCellConnectionStatus(PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING)
+                .setCellBandwidthDownlinkKhz(2000 /* bandwidth */)
+                .build();
+
+        List<PhysicalChannelConfig> configs = Collections.singletonList(config);
+
+        ((IPhoneStateListener) field.get(mPhoneStateListenerUT))
+            .onPhysicalChannelConfigurationChanged(configs);
+
+        assertTrue(mPhysicalChannelConfigs.equals(configs));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
index 1d4b173..729260e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
@@ -128,8 +128,10 @@
 
         assertNull(mPhysicalChannelConfigs);
 
-        PhysicalChannelConfig config = new PhysicalChannelConfig(
-                PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING, 20000 /* bandwidth */);
+        PhysicalChannelConfig config = new PhysicalChannelConfig.Builder()
+                .setCellConnectionStatus(PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING)
+                .setCellBandwidthDownlinkKhz(20000 /* bandwidth */)
+                .build();
 
         List<PhysicalChannelConfig> configs = Collections.singletonList(config);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 4f450d0..56bf746 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 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;
@@ -103,43 +104,34 @@
         // verify nothing has been done while there are no inputs
         assertFalse("data allowed initially", mDataAllowed[0]);
         assertFalse("data allowed initially", mDataAllowed[0]);
-        assertFalse("phone active initially", mPhoneSwitcher.isPhoneActive(0));
+        assertFalse("phone active initially", mPhoneSwitcher.shouldApplySpecifiedRequests(0));
 
         NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
         waitABit();
 
         assertFalse("data allowed after request", mDataAllowed[0]);
-        assertFalse("phone active after request", mPhoneSwitcher.isPhoneActive(0));
+        assertFalse("phone active after request", mPhoneSwitcher.shouldApplySpecifiedRequests(0));
 
         // not registered yet - shouldn't inc
         verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
 
-        boolean threw = false;
-        try {
-            // should throw
-            mPhoneSwitcher.registerForActivePhoneSwitch(2, mActivePhoneSwitchHandler,
-                    ACTIVE_PHONE_SWITCH, null);
-        } catch (IllegalArgumentException e) {
-            threw = true;
-        }
-        assertTrue("register with bad phoneId didn't throw", threw);
-
-        mPhoneSwitcher.registerForActivePhoneSwitch(0, mActivePhoneSwitchHandler,
+        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
 
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
-
+        clearInvocations(mActivePhoneSwitchHandler);
 
         setDefaultDataSubId(0);
 
-        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
         assertFalse("data allowed", mDataAllowed[0]);
 
         setSlotIndexToSubId(0, 0);
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertTrue("data not allowed", mDataAllowed[0]);
 
         // now try various things that should cause the active phone to switch:
@@ -158,14 +150,16 @@
         setDefaultDataSubId(1);
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(3)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
 
         setSlotIndexToSubId(1, 1);
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(3)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertTrue("data not allowed", mDataAllowed[1]);
 
@@ -173,7 +167,8 @@
         setDefaultDataSubId(0);
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(4)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[1]);
         assertTrue("data not allowed", mDataAllowed[0]);
 
@@ -182,7 +177,8 @@
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(5)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -191,7 +187,8 @@
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(6)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -199,7 +196,8 @@
         releaseNetworkRequest(internetNetworkRequest);
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(7)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -207,7 +205,8 @@
         NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(8)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -216,7 +215,8 @@
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(9)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -225,7 +225,8 @@
         mSubChangedListener.onSubscriptionsChanged();
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(10)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertTrue("data not allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -233,7 +234,8 @@
         releaseNetworkRequest(specificInternetRequest);
         waitABit();
 
-        verify(mActivePhoneSwitchHandler, times(11)).sendMessageAtTime(any(), anyLong());
+        verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertFalse("data allowed", mDataAllowed[1]);
 
@@ -288,7 +290,7 @@
         setSlotIndexToSubId(1, 1);
         setDefaultDataSubId(0);
         waitABit();
-        mPhoneSwitcher.registerForActivePhoneSwitch(0, mActivePhoneSwitchHandler,
+        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
                 ACTIVE_PHONE_SWITCH, null);
         waitABit();
         // verify initial conditions
@@ -321,6 +323,8 @@
         initialize(numPhones, maxActivePhones);
 
         addInternetNetworkRequest(null, 50);
+        waitABit();
+
         setSlotIndexToSubId(0, 0);
         setSlotIndexToSubId(1, 1);
         setDefaultDataSubId(0);
@@ -388,6 +392,81 @@
         mHandlerThread.quit();
     }
 
+    @Test
+    @SmallTest
+    public void testSetPreferredDataModemCommand() throws Exception {
+        final int numPhones = 2;
+        final int maxActivePhones = 1;
+        doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
+        initialize(numPhones, maxActivePhones);
+        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+                ACTIVE_PHONE_SWITCH, null);
+        mPhoneSwitcher.registerForActivePhoneSwitch(mActivePhoneSwitchHandler,
+                ACTIVE_PHONE_SWITCH, null);
+        verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+        clearInvocations(mActivePhoneSwitchHandler);
+
+        // 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);
+        waitABit();
+        // Phone 0 (sub 1) should preferredDataModem it has default data sub.
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+        verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(0));
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(1));
+        assertTrue(mPhoneSwitcher.shouldApplyUnspecifiedRequests(0));
+        assertFalse(mPhoneSwitcher.shouldApplyUnspecifiedRequests(1));
+
+        clearInvocations(mMockRadioConfig);
+        clearInvocations(mActivePhoneSwitchHandler);
+
+        // Notify phoneSwitcher about default data sub and default network request.
+        // It shouldn't change anything.
+        addInternetNetworkRequest(null, 50);
+        addMmsNetworkRequest(2);
+        waitABit();
+        verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
+        verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(0));
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(1));
+        assertTrue(mPhoneSwitcher.shouldApplyUnspecifiedRequests(0));
+        assertFalse(mPhoneSwitcher.shouldApplyUnspecifiedRequests(1));
+
+        // Set sub 2 as preferred sub should make phone 1 preferredDataModem
+        mPhoneSwitcher.setPreferredData(2);
+        waitABit();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
+        verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(0));
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(1));
+        assertFalse(mPhoneSwitcher.shouldApplyUnspecifiedRequests(0));
+        assertTrue(mPhoneSwitcher.shouldApplyUnspecifiedRequests(1));
+
+        clearInvocations(mMockRadioConfig);
+        clearInvocations(mActivePhoneSwitchHandler);
+
+        // Unset preferred sub should make phone0 preferredDataModem again.
+        mPhoneSwitcher.setPreferredData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+        waitABit();
+        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+        verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(0));
+        assertTrue(mPhoneSwitcher.shouldApplySpecifiedRequests(1));
+        assertTrue(mPhoneSwitcher.shouldApplyUnspecifiedRequests(0));
+        assertFalse(mPhoneSwitcher.shouldApplyUnspecifiedRequests(1));
+
+        // SetDataAllowed should never be triggered.
+        verify(mCommandsInterface0, never()).setDataAllowed(anyBoolean(), any());
+        verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
+
+        mHandlerThread.quit();
+
+    }
+
     /* Private utility methods start here */
 
     private void sendDefaultDataSubChanged() {
@@ -396,9 +475,6 @@
     }
 
     private void initialize(int numPhones, int maxActivePhones) throws Exception {
-        mHandlerThread = new HandlerThread("PhoneSwitcherTestThread");
-        mHandlerThread.start();
-
         mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
                 sNetworkAttributes);
 
@@ -409,9 +485,17 @@
         initializeTelRegistryMock();
         initializeConnManagerMock();
 
-        mPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
-                mContext, mSubscriptionController, mHandlerThread.getLooper(),
-                mTelRegistryMock, mCommandsInterfaces, mPhones);
+        mHandlerThread = new HandlerThread("PhoneSwitcherTestThread") {
+            @Override
+            public void onLooperPrepared() {
+                mPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
+                        mContext, mSubscriptionController, this.getLooper(),
+                        mTelRegistryMock, mCommandsInterfaces, mPhones);
+            }
+        };
+
+        mHandlerThread.start();
+        waitABit();
 
         verify(mTelRegistryMock).addOnSubscriptionsChangedListener(
                 eq(mContext.getOpPackageName()), any());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java
new file mode 100644
index 0000000..535f7b2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.PhysicalChannelConfig.Builder;
+import android.telephony.ServiceState;
+
+import org.junit.Test;
+
+/** Unit test for {@link android.telephony.PhysicalChannelConfig}. */
+public class PhysicalChannelConfigTest {
+
+    private static final int RAT = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+    private static final int CONNECTION_STATUS = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+    private static final int CELL_BANDWIDTH = 12345;
+    private static final int FREQUENCY_RANGE = 1;
+    private static final int CHANNEL_NUMBER = 1234;
+    private static final int[] CONTEXT_IDS = new int[] {123, 555, 1, 0};
+    private static final int PHYSICAL_CELL_ID = 502;
+
+    @Test
+    public void testBuilder() {
+        PhysicalChannelConfig config = new Builder()
+                .setRat(RAT)
+                .setCellConnectionStatus(CONNECTION_STATUS)
+                .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+                .setFrequencyRange(FREQUENCY_RANGE)
+                .setChannelNumber(CHANNEL_NUMBER)
+                .setContextIds(CONTEXT_IDS)
+                .setPhysicalCellId(PHYSICAL_CELL_ID)
+                .build();
+
+        assertThat(config.getRat()).isEqualTo(RAT);
+        assertThat(config.getConnectionStatus()).isEqualTo(CONNECTION_STATUS);
+        assertThat(config.getCellBandwidthDownlink()).isEqualTo(CELL_BANDWIDTH);
+        assertThat(config.getFrequencyRange()).isEqualTo(FREQUENCY_RANGE);
+        assertThat(config.getChannelNumber()).isEqualTo(CHANNEL_NUMBER);
+        assertThat(config.getContextIds()).isEqualTo(CONTEXT_IDS);
+        assertThat(config.getPhysicalCellId()).isEqualTo(PHYSICAL_CELL_ID);
+    }
+
+    @Test
+    public void testParcel() {
+        PhysicalChannelConfig config = new Builder()
+                .setRat(RAT)
+                .setCellConnectionStatus(CONNECTION_STATUS)
+                .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+                .setFrequencyRange(FREQUENCY_RANGE)
+                .setChannelNumber(CHANNEL_NUMBER)
+                .setContextIds(CONTEXT_IDS)
+                .setPhysicalCellId(PHYSICAL_CELL_ID)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        config.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+
+        PhysicalChannelConfig fromParcel = PhysicalChannelConfig.CREATOR.createFromParcel(parcel);
+
+        assertThat(fromParcel).isEqualTo(config);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 6bdb23b..f528ac4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -229,7 +229,7 @@
     private static final int MTU = 1234;
     private static final String MVNO_TYPE = "";
     private static final String MVNO_MATCH_DATA = "";
-    private static final boolean MODEM_COGNITIVE = true;
+    private static final boolean PERSISTENT = true;
 
     private class RILTestHandler extends HandlerThread {
 
@@ -704,7 +704,7 @@
                 null, null, -1, "", "", 0, ApnSetting.TYPE_DUN, ApnSetting.PROTOCOL_IP,
                 ApnSetting.PROTOCOL_IP, true, 0, 0, false, 0, 0, 0, 0, -1, "");
         DataProfile dataProfile = DcTracker.createDataProfile(
-                apnSetting, apnSetting.getProfileId());
+                apnSetting, apnSetting.getProfileId(), false);
         boolean isRoaming = false;
 
         mRILUnderTest.setInitialAttachApn(dataProfile, isRoaming, obtainMessage());
@@ -715,7 +715,7 @@
                         "convertToHalDataProfile",
                         new Class<?>[] {DataProfile.class},
                         new Object[] {dataProfile})),
-                eq(dataProfile.isModemCognitive()),
+                eq(dataProfile.isPersistent()),
                 eq(isRoaming));
         verifyRILResponse(
                 mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SET_INITIAL_ATTACH_APN);
@@ -1110,7 +1110,6 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, Integer.MAX_VALUE, MCC_STR,
                 MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1118,6 +1117,7 @@
         expected.setCellIdentity(cil);
         expected.setCellSignalStrength(css);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoLte);
     }
 
@@ -1151,7 +1151,6 @@
         CellInfoGsm expected = new CellInfoGsm();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
@@ -1159,6 +1158,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoGsm);
     }
 
@@ -1191,7 +1191,6 @@
         CellInfoWcdma expected = new CellInfoWcdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
@@ -1199,6 +1198,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoWcdma);
     }
 
@@ -1232,7 +1232,6 @@
         CellInfoTdscdma expected = new CellInfoTdscdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
         CellIdentityTdscdma ci = new CellIdentityTdscdma(
                 MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
@@ -1240,6 +1239,7 @@
                 SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
+        cellInfoTdscdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoTdscdma);
     }
 
@@ -1274,7 +1274,6 @@
         CellInfoCdma expected = new CellInfoCdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityCdma ci = new CellIdentityCdma(
                 NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
                 EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
@@ -1283,6 +1282,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
+        cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoCdma);
     }
 
@@ -1295,7 +1295,6 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityLte cil = new CellIdentityLte(
                 CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1303,6 +1302,7 @@
         expected.setCellIdentity(cil);
         expected.setCellSignalStrength(css);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoLte);
     }
 
@@ -1316,7 +1316,6 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, BANDWIDTH, MCC_STR, MNC_STR,
                 EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1324,6 +1323,7 @@
         expected.setCellIdentity(cil);
         expected.setCellSignalStrength(css);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoLte);
     }
 
@@ -1339,7 +1339,6 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityLte cil = new CellIdentityLte(
                 CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1347,6 +1346,7 @@
         expected.setCellIdentity(cil);
         expected.setCellSignalStrength(css);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoLte.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoLte);
     }
 
@@ -1359,7 +1359,6 @@
         CellInfoGsm expected = new CellInfoGsm();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
@@ -1367,6 +1366,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoGsm);
     }
 
@@ -1380,7 +1380,6 @@
         CellInfoGsm expected = new CellInfoGsm();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
@@ -1388,6 +1387,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoGsm);
     }
 
@@ -1403,7 +1403,6 @@
         CellInfoGsm expected = new CellInfoGsm();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
@@ -1411,6 +1410,7 @@
         expected.setCellIdentity(ci);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
         expected.setCellSignalStrength(cs);
+        cellInfoGsm.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoGsm);
     }
 
@@ -1424,7 +1424,6 @@
         CellInfoWcdma expected = new CellInfoWcdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthWcdma cs =
@@ -1432,6 +1431,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoWcdma);
     }
 
@@ -1445,7 +1445,6 @@
         CellInfoWcdma expected = new CellInfoWcdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
         CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
@@ -1453,6 +1452,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoWcdma);
     }
 
@@ -1468,7 +1468,6 @@
         CellInfoWcdma expected = new CellInfoWcdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityWcdma ci = new CellIdentityWcdma(
                 LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
         CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
@@ -1476,6 +1475,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoWcdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoWcdma);
     }
 
@@ -1488,7 +1488,6 @@
         CellInfoCdma expected = new CellInfoCdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityCdma ci = new CellIdentityCdma(
                 NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
                 ALPHA_LONG, ALPHA_SHORT);
@@ -1497,6 +1496,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoCdma);
     }
 
@@ -1509,7 +1509,6 @@
         CellInfoCdma expected = new CellInfoCdma();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityCdma ci = new CellIdentityCdma(
                 NETWORK_ID, SYSTEM_ID, BASESTATION_ID, LONGITUDE, LATITUDE,
                 EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
@@ -1518,6 +1517,7 @@
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+        cellInfoCdma.setTimeStamp(TIMESTAMP); // override the timestamp
         assertEquals(expected, cellInfoCdma);
     }
 
@@ -1727,7 +1727,7 @@
 
         DataProfile dp = new DataProfile(PROFILE_ID, APN, PROTOCOL, AUTH_TYPE, USER_NAME, PASSWORD,
                 TYPE, MAX_CONNS_TIME, MAX_CONNS, WAIT_TIME, APN_ENABLED, SUPPORTED_APNT_YPES_BITMAP,
-                ROAMING_PROTOCOL, BEARER_BITMAP, MTU, MVNO_TYPE, MVNO_MATCH_DATA, MODEM_COGNITIVE);
+                ROAMING_PROTOCOL, BEARER_BITMAP, MTU, PERSISTENT, false);
         mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
                 false, 0, null, obtainMessage());
         ArgumentCaptor<DataProfileInfo> dpiCaptor = ArgumentCaptor.forClass(DataProfileInfo.class);
@@ -1752,7 +1752,5 @@
         assertEquals(ROAMING_PROTOCOL, dpi.protocol);
         assertEquals(BEARER_BITMAP, dpi.bearerBitmap);
         assertEquals(MTU, dpi.mtu);
-        assertEquals(0, dpi.mvnoType);
-        assertEquals(MVNO_MATCH_DATA, dpi.mvnoMatchData);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 02e62d2..16e0656 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -291,8 +291,7 @@
 
         NetworkRegistrationState wwanDataRegState = new NetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
-                0, 0, 0, false,
-                null, null, 0);
+                0, 0, 0, false, null, null, 0, false, false);
 
         NetworkRegistrationState wlanRegState = new NetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN,
@@ -314,8 +313,7 @@
 
         wwanDataRegState = new NetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
-                0, 0, 0, true,
-                null, null, 0);
+                0, 0, 0, true, null, null, 0, false, false);
         ss.addNetworkRegistrationState(wwanDataRegState);
         assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
                 AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index e631269..091d6dd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
@@ -39,6 +38,7 @@
 import android.app.IAlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -67,6 +67,7 @@
 import android.telephony.CellIdentityLte;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
+import android.telephony.INetworkService;
 import android.telephony.NetworkRegistrationState;
 import android.telephony.NetworkService;
 import android.telephony.PhysicalChannelConfig;
@@ -83,7 +84,6 @@
 
 import com.android.internal.R;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
-import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
 
@@ -101,9 +101,6 @@
 import java.util.List;
 
 public class ServiceStateTrackerTest extends TelephonyTest {
-
-    @Mock
-    private DcTracker mDct;
     @Mock
     private ProxyController mProxyController;
     @Mock
@@ -111,7 +108,12 @@
     @Mock
     protected IAlarmManager mAlarmManager;
 
-    CellularNetworkService mCellularNetworkService;
+    private CellularNetworkService mCellularNetworkService;
+
+    @Mock
+    private NetworkService mIwlanNetworkService;
+    @Mock
+    private INetworkService.Stub mIwlanNetworkServiceStub;
 
     private ServiceStateTracker sst;
     private ServiceStateTrackerTestHandler mSSTTestHandler;
@@ -145,17 +147,33 @@
 
     private void addNetworkService() {
         mCellularNetworkService = new CellularNetworkService();
-        ServiceInfo serviceInfo =  new ServiceInfo();
-        serviceInfo.packageName = "com.android.phone";
-        serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
-        IntentFilter filter = new IntentFilter();
+        ServiceInfo CellularServiceInfo = new ServiceInfo();
+        CellularServiceInfo.packageName = "com.android.phone";
+        CellularServiceInfo.name = "CellularNetworkService";
+        CellularServiceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
+        IntentFilter cellularIntentfilter = new IntentFilter();
         mContextFixture.addService(
                 NetworkService.NETWORK_SERVICE_INTERFACE,
-                null,
+                new ComponentName("com.android.phone",
+                        "com.android.internal.telephony.CellularNetworkService"),
                 "com.android.phone",
                 mCellularNetworkService.mBinder,
-                serviceInfo,
-                filter);
+                CellularServiceInfo,
+                cellularIntentfilter);
+
+        ServiceInfo iwlanServiceInfo = new ServiceInfo();
+        iwlanServiceInfo.packageName = "com.xyz.iwlan.networkservice";
+        iwlanServiceInfo.name = "IwlanNetworkService";
+        iwlanServiceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
+        IntentFilter iwlanIntentFilter = new IntentFilter();
+        mContextFixture.addService(
+                NetworkService.NETWORK_SERVICE_INTERFACE,
+                new ComponentName("com.xyz.iwlan.networkservice",
+                        "com.xyz.iwlan.IwlanNetworkService"),
+                "com.xyz.iwlan.networkservice",
+                mIwlanNetworkServiceStub,
+                iwlanServiceInfo,
+                iwlanIntentFilter);
     }
 
     @Before
@@ -166,10 +184,12 @@
 
         mContextFixture.putResource(R.string.config_wwan_network_service_package,
                 "com.android.phone");
+        mContextFixture.putResource(R.string.config_wlan_network_service_package,
+                "com.xyz.iwlan.networkservice");
+        doReturn(mIwlanNetworkServiceStub).when(mIwlanNetworkServiceStub).asBinder();
         addNetworkService();
 
-        doReturn(true).when(mDct).isDisconnected();
-        mPhone.mDcTracker = mDct;
+        doReturn(true).when(mDcTracker).isDisconnected();
 
         replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
         mBundle = mContextFixture.getCarrierConfigBundle();
@@ -190,6 +210,7 @@
 
         int dds = SubscriptionManager.getDefaultDataSubscriptionId();
         doReturn(dds).when(mPhone).getSubId();
+        doReturn(true).when(mPhone).areAllDataDisconnected();
 
         mSSTTestHandler = new ServiceStateTrackerTestHandler(getClass().getSimpleName());
         mSSTTestHandler.start();
@@ -209,10 +230,11 @@
     @Test
     @MediumTest
     public void testSetRadioPower() {
-        boolean oldState = mSimulatedCommands.getRadioState().isOn();
+        boolean oldState = (mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
         sst.setRadioPower(!oldState);
         waitForMs(100);
-        assertTrue(oldState != mSimulatedCommands.getRadioState().isOn());
+        assertTrue(oldState
+                != (mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON));
     }
 
     @Test
@@ -220,11 +242,11 @@
     public void testSetRadioPowerOffUnderDataConnected() {
         sst.setRadioPower(true);
         waitForMs(100);
-        doReturn(false).when(mDct).isDisconnected();
+        doReturn(false).when(mPhone).areAllDataDisconnected();
         sst.setRadioPower(false);
         waitForMs(200);
         verify(this.mProxyController, times(1)).registerForAllDataDisconnected(anyInt(),
-                 eq(sst), anyInt(), anyObject());
+                 eq(sst), anyInt());
     }
 
     @Test
@@ -233,21 +255,23 @@
         // Carrier disable radio power
         sst.setRadioPowerFromCarrier(false);
         waitForMs(100);
-        assertFalse(mSimulatedCommands.getRadioState().isOn());
+        assertFalse(mSimulatedCommands.getRadioState()
+                == TelephonyManager.RADIO_POWER_ON);
         assertTrue(sst.getDesiredPowerState());
         assertFalse(sst.getPowerStateFromCarrier());
 
         // User toggle radio power will not overrides carrier settings
         sst.setRadioPower(true);
         waitForMs(100);
-        assertFalse(mSimulatedCommands.getRadioState().isOn());
+        assertFalse(mSimulatedCommands.getRadioState()
+                == TelephonyManager.RADIO_POWER_ON);
         assertTrue(sst.getDesiredPowerState());
         assertFalse(sst.getPowerStateFromCarrier());
 
         // Carrier re-enable radio power
         sst.setRadioPowerFromCarrier(true);
         waitForMs(100);
-        assertTrue(mSimulatedCommands.getRadioState().isOn());
+        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
         assertTrue(sst.getDesiredPowerState());
         assertTrue(sst.getPowerStateFromCarrier());
 
@@ -255,7 +279,8 @@
         sst.setRadioPower(false);
         sst.setRadioPowerFromCarrier(true);
         waitForMs(100);
-        assertFalse(mSimulatedCommands.getRadioState().isOn());
+        assertFalse(mSimulatedCommands.getRadioState()
+                == TelephonyManager.RADIO_POWER_ON);
         assertFalse(sst.getDesiredPowerState());
         assertTrue(sst.getPowerStateFromCarrier());
     }
@@ -1593,7 +1618,8 @@
 
         sst.requestShutdown();
         waitForMs(100);
-        assertFalse(mSimulatedCommands.getRadioState().isAvailable());
+        assertFalse(mSimulatedCommands.getRadioState()
+                != TelephonyManager.RADIO_POWER_UNAVAILABLE);
     }
 
     @Test
@@ -1606,10 +1632,11 @@
         mSimulatedCommands.setRadioPowerFailResponse(true);
         sst.setRadioPower(false);
         waitForMs(100);
-        assertTrue(mSimulatedCommands.getRadioState().isOn());
+        assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
         sst.requestShutdown();
         waitForMs(100);
-        assertFalse(mSimulatedCommands.getRadioState().isAvailable());
+        assertFalse(mSimulatedCommands.getRadioState()
+                != TelephonyManager.RADIO_POWER_UNAVAILABLE);
     }
 
     @Test
@@ -1646,7 +1673,7 @@
 
     private void changeRegState(int state, CellIdentity cid, int voiceRat, int dataRat) {
         NetworkRegistrationState dataResult = new NetworkRegistrationState(
-                0, 0, state, dataRat, 0, false, null, cid, 1);
+                0, 0, state, dataRat, 0, false, null, cid, 1, false, false);
         sst.mPollingContext[0] = 2;
         // update data reg state to be in service
         sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
@@ -1719,7 +1746,10 @@
         ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
         int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
         for (int bw : bandwidths) {
-            pc.add(new PhysicalChannelConfig(ssType, bw));
+            pc.add(new PhysicalChannelConfig.Builder()
+                    .setCellConnectionStatus(ssType)
+                    .setCellBandwidthDownlinkKhz(bw)
+                    .build());
 
             // All cells after the first are secondary serving cells.
             ssType = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
@@ -1731,7 +1761,8 @@
 
     private void sendRegStateUpdateForLteCellId(CellIdentityLte cellId) {
         NetworkRegistrationState dataResult = new NetworkRegistrationState(
-                2, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1);
+                2, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1,
+                false, false);
         NetworkRegistrationState voiceResult = new NetworkRegistrationState(
                 1, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId,
                 false, 0, 0, 0);
@@ -1794,7 +1825,8 @@
     public void testPhyChanBandwidthResetsOnOos() throws Exception {
         testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
         NetworkRegistrationState dataResult = new NetworkRegistrationState(
-                2, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1);
+                2, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1, false,
+                false);
         NetworkRegistrationState voiceResult = new NetworkRegistrationState(
                 1, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null,
                 false, 0, 0, 0);
@@ -1807,4 +1839,49 @@
         waitForMs(200);
         assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
     }
+
+    @Test
+    @SmallTest
+    public void testGetMdn() throws Exception {
+        doReturn(false).when(mPhone).isPhoneTypeGsm();
+        doReturn(false).when(mPhone).isPhoneTypeCdma();
+        doReturn(true).when(mPhone).isPhoneTypeCdmaLte();
+        doReturn(CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM).when(mCdmaSSM)
+                .getCdmaSubscriptionSource();
+
+        logd("Calling updatePhoneType");
+        // switch to CDMA
+        sst.updatePhoneType();
+
+        // trigger RUIM_RECORDS_LOADED
+        ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mRuimRecords).registerForRecordsLoaded(eq(sst), integerArgumentCaptor.capture(),
+                nullable(Object.class));
+
+        // response for mRuimRecords.registerForRecordsLoaded()
+        Message msg = Message.obtain();
+        msg.what = integerArgumentCaptor.getValue();
+        msg.obj = new AsyncResult(null, null, null);
+        sst.sendMessage(msg);
+
+        // wait for RUIM_RECORDS_LOADED to be handled
+        waitForHandlerAction(sst, 5000);
+
+        // mdn should be null as nothing populated it
+        assertEquals(null, sst.getMdnNumber());
+
+        // if ruim is provisioned, mdn should still be null
+        doReturn(true).when(mRuimRecords).isProvisioned();
+        assertEquals(null, sst.getMdnNumber());
+
+        // if ruim is not provisioned, and mdn is non null, sst should still return null
+        doReturn(false).when(mRuimRecords).isProvisioned();
+        String mockMdn = "mockMdn";
+        doReturn(mockMdn).when(mRuimRecords).getMdn();
+        assertEquals(null, sst.getMdnNumber());
+
+        // if ruim is provisioned, and mdn is non null, sst should also return the correct value
+        doReturn(true).when(mRuimRecords).isProvisioned();
+        assertEquals(mockMdn, sst.getMdnNumber());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
index 3cd5239..0b33c80 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
@@ -35,9 +35,9 @@
     @Test
     public void testDefaults() throws Exception {
         SignalStrength s = new SignalStrength();
-        assertEquals(-1, s.getCdmaDbm());
+        assertEquals(SignalStrength.INVALID, s.getCdmaDbm());
         assertEquals(-1, s.getCdmaEcio());
-        assertEquals(-1, s.getEvdoDbm());
+        assertEquals(SignalStrength.INVALID, s.getEvdoDbm());
         assertEquals(-1, s.getEvdoEcio());
         assertEquals(-1, s.getEvdoSnr());
         assertEquals(-1, s.getGsmBitErrorRate());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 489a19f..8e124fa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
@@ -27,6 +28,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.Manifest;
+import android.content.ContentValues;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -49,6 +52,7 @@
     private String mCallingPackage;
     private SubscriptionController mSubscriptionControllerUT;
     private MockContentResolver mMockContentResolver;
+    private FakeTelephonyProvider mFakeTelephonyProvider;
     @Mock
     private ITelephonyRegistry.Stub mTelephonyRegisteryMock;
 
@@ -70,13 +74,15 @@
 
         mSubscriptionControllerUT.getInstance().updatePhonesAvailability(new Phone[]{mPhone});
         mMockContentResolver = (MockContentResolver) mContext.getContentResolver();
+        mFakeTelephonyProvider = new FakeTelephonyProvider();
         mMockContentResolver.addProvider(SubscriptionManager.CONTENT_URI.getAuthority(),
-                new FakeTelephonyProvider());
+                mFakeTelephonyProvider);
 
     }
 
     @After
     public void tearDown() throws Exception {
+        mContextFixture.addCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
         /* should clear fake content provider and resolver here */
         mContext.getContentResolver().delete(SubscriptionManager.CONTENT_URI, null, null);
 
@@ -129,6 +135,7 @@
         String disName = "TESTING";
         String disNum = "12345";
         boolean isOpportunistic = true;
+        boolean isMetered = false;
 
         testInsertSim();
         /* Get SUB ID */
@@ -136,22 +143,30 @@
         assertTrue(subIds != null && subIds.length != 0);
         int subID = subIds[0];
 
+        /* Getting, there is no direct getter function for each fields of property */
+        SubscriptionInfo subInfo = mSubscriptionControllerUT
+                .getActiveSubscriptionInfo(subID, mCallingPackage);
+        //isMetered should initialize as true
+        assertTrue(subInfo.isMetered());
+
         /* Setting */
         mSubscriptionControllerUT.setDisplayName(disName, subID);
         mSubscriptionControllerUT.setDataRoaming(dataRoaming, subID);
         mSubscriptionControllerUT.setDisplayNumber(disNum, subID);
         mSubscriptionControllerUT.setIconTint(iconTint, subID);
         mSubscriptionControllerUT.setOpportunistic(isOpportunistic, subID);
+        mSubscriptionControllerUT.setMetered(isMetered, subID);
 
-        /* Getting, there is no direct getter function for each fields of property */
-        SubscriptionInfo subInfo = mSubscriptionControllerUT
-                .getActiveSubscriptionInfo(subID, mCallingPackage);
+        subInfo = mSubscriptionControllerUT
+            .getActiveSubscriptionInfo(subID, mCallingPackage);
+
         assertNotNull(subInfo);
         assertEquals(dataRoaming, subInfo.getDataRoaming());
         assertEquals(disName, subInfo.getDisplayName());
         assertEquals(iconTint, subInfo.getIconTint());
         assertEquals(disNum, subInfo.getNumber());
         assertEquals(isOpportunistic, subInfo.isOpportunistic());
+        assertEquals(isMetered, subInfo.isMetered());
 
         /* verify broadcast intent */
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
@@ -343,12 +358,12 @@
                 .notifyOpportunisticSubscriptionInfoChanged();
 
         testInsertSim();
-        testInsertSim2();
+        mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
 
         // Neither sub1 or sub2 are opportunistic. So getOpportunisticSubscriptions
         // should return empty list and no callback triggered.
         List<SubscriptionInfo> opptSubList = mSubscriptionControllerUT
-                .getOpportunisticSubscriptions(0, mCallingPackage);
+                .getOpportunisticSubscriptions(mCallingPackage);
 
         assertTrue(opptSubList.isEmpty());
         verify(mTelephonyRegisteryMock, times(0))
@@ -360,7 +375,7 @@
         verify(mTelephonyRegisteryMock, times(1))
                 .notifyOpportunisticSubscriptionInfoChanged();
         opptSubList = mSubscriptionControllerUT
-                .getOpportunisticSubscriptions(0, mCallingPackage);
+                .getOpportunisticSubscriptions(mCallingPackage);
         assertEquals(1, opptSubList.size());
         assertEquals("test2", opptSubList.get(0).getIccId());
 
@@ -374,16 +389,84 @@
                 .notifyOpportunisticSubscriptionInfoChanged();
     }
 
-    private void testInsertSim2() {
-        // verify there's already a SIM profile added.
-        assertEquals(1, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage));
+    @Test
+    @SmallTest
+    public void testSetSubscriptionGroupWithModifyPermission() throws Exception {
+        testInsertSim();
+        mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
 
-        int slotID = 0;
-        //insert one Subscription Info
-        mSubscriptionControllerUT.addSubInfoRecord("test2", slotID);
+        int[] subIdList = new int[] {1, 2};
+        // It should fail since it has no permission.
+        String groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertEquals(null, groupId);
 
-        //verify there is one sim
-        assertEquals(2, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage));
+        // With modify permission it should succeed.
+        mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertNotEquals(null, groupId);
+
+        // Calling it again should generate a new group ID.
+        String newGroupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertNotEquals(null, newGroupId);
+        assertNotEquals(groupId, newGroupId);
+
+        // SubId 6 doesn't exist. Should fail.
+        subIdList = new int[] {1, 6};
+        mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+        groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertEquals(null, groupId);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
+        testInsertSim();
+        // Adding a second profile and mark as embedded.
+        mSubscriptionControllerUT.addSubInfoRecord("test2", 0);
+        ContentValues values = new ContentValues();
+        values.put(SubscriptionManager.IS_EMBEDDED, 1);
+        mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
+                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
+        mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
+
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+
+        int[] subIdList = new int[] {1, 2};
+        // It should fail since it has no permission.
+        String groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertEquals(null, groupId);
+
+        // With modify permission it should succeed.
+        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
+        groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertEquals(null, groupId);
+
+        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
+        groupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertNotEquals(null, groupId);
+
+        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT
+                .getActiveSubscriptionInfoList(mContext.getOpPackageName());
+
+        // Revoke carrier privilege of sub 2 but make it manageable by caller.
+        doReturn(false).when(mTelephonyManager).hasCarrierPrivileges(2);
+        doReturn(true).when(mSubscriptionManager).canManageSubscription(
+                eq(subInfoList.get(1)), anyString());
+
+        String newGroupId = mSubscriptionControllerUT.setSubscriptionGroup(
+                subIdList, mContext.getOpPackageName());
+        assertNotEquals(null, newGroupId);
+        assertNotEquals(groupId, newGroupId);
     }
 
     private void registerMockTelephonyRegistry() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 4466378..60bbff9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -36,6 +36,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.IPackageManager;
 import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.HandlerThread;
@@ -90,6 +91,8 @@
     private EuiccController mEuiccController;
     @Mock
     private IntentBroadcaster mIntentBroadcaster;
+    @Mock
+    private IPackageManager mPackageManager;
 
     /*Custom ContentProvider */
     private class FakeSubscriptionContentProvider extends MockContentProvider {
@@ -108,7 +111,7 @@
         @Override
         public void onLooperPrepared() {
             mUpdater = new SubscriptionInfoUpdater(getLooper(), mContext, new Phone[]{mPhone},
-                    new CommandsInterface[]{mSimulatedCommands});
+                    new CommandsInterface[]{mSimulatedCommands}, mPackageManager);
             setReady(true);
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 42a8ad1..fc2b632 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -16,13 +16,19 @@
 package com.android.internal.telephony;
 
 import static android.telephony.PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE;
+import static android.telephony.PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE;
+import static android.telephony.PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 
 import android.os.HandlerThread;
 import android.os.ServiceManager;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.TelephonyRegistry;
@@ -38,13 +44,26 @@
     private PhoneStateListener mPhoneStateListener;
     private TelephonyRegistry mTelephonyRegistry;
     private PhoneCapability mPhoneCapability;
+    private int mPreferredSubId;
+    private int mSrvccState = -1;
 
     public class PhoneStateListenerWrapper extends PhoneStateListener {
         @Override
+        public void onSrvccStateChanged(int srvccState) {
+            mSrvccState = srvccState;
+            setReady(true);
+        }
+
+        @Override
         public void onPhoneCapabilityChanged(PhoneCapability capability) {
             mPhoneCapability = capability;
             setReady(true);
         }
+        @Override
+        public void onPreferredDataSubIdChanged(int preferredSubId) {
+            mPreferredSubId = preferredSubId;
+            setReady(true);
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -64,6 +83,8 @@
     @Before
     public void setUp() throws Exception {
         super.setUp("TelephonyRegistryTest");
+        // ServiceManager.getService("isub") will return this stub for any call to
+        // SubscriptionManager.
         mServiceManagerMockedServices.put("isub", mISubStub);
         mHandlerThread.start();
         waitUntilReady();
@@ -97,4 +118,74 @@
         waitUntilReady();
         assertEquals(phoneCapability, mPhoneCapability);
     }
+
+
+    @Test @SmallTest
+    public void testPreferredDataSubChanged() {
+        // mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately.
+        setReady(false);
+        int preferredSubId = 0;
+        mTelephonyRegistry.notifyPreferredDataSubIdChanged(preferredSubId);
+        mTelephonyRegistry.listen(mContext.getOpPackageName(),
+                mPhoneStateListener.callback,
+                LISTEN_PREFERRED_DATA_SUBID_CHANGE, true);
+        waitUntilReady();
+        assertEquals(preferredSubId, mPreferredSubId);
+
+        // notifyPhoneCapabilityChanged with a new capability. Callback should be triggered.
+        setReady(false);
+        mPreferredSubId = 1;
+        mTelephonyRegistry.notifyPreferredDataSubIdChanged(preferredSubId);
+        waitUntilReady();
+        assertEquals(preferredSubId, mPreferredSubId);
+    }
+
+    /**
+     * Test that we first receive a callback when listen(...) is called that contains the latest
+     * notify(...) response and then that the callback is called correctly when notify(...) is
+     * called.
+     */
+    @Test
+    @SmallTest
+    public void testSrvccStateChanged() throws Exception {
+        // Return a phone ID of 0 for all sub ids given.
+        doReturn(0/*phoneId*/).when(mISubStub).getPhoneId(anyInt());
+        setReady(false);
+        int srvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+        mTelephonyRegistry.notifySrvccStateChanged(0 /*subId*/, srvccState);
+        // Should receive callback when listen is called that contains the latest notify result.
+        mTelephonyRegistry.listenForSubscriber(0 /*subId*/, mContext.getOpPackageName(),
+                mPhoneStateListener.callback,
+                LISTEN_SRVCC_STATE_CHANGED, true);
+        waitUntilReady();
+        assertEquals(srvccState, mSrvccState);
+
+        // trigger callback
+        setReady(false);
+        srvccState = TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED;
+        mTelephonyRegistry.notifySrvccStateChanged(0 /*subId*/, srvccState);
+        waitUntilReady();
+        assertEquals(srvccState, mSrvccState);
+    }
+
+    /**
+     * Test that a SecurityException is thrown when we try to listen to a SRVCC state change without
+     * READ_PRIVILEGED_PHONE_STATE.
+     */
+    @Test
+    @SmallTest
+    public void testSrvccStateChangedNoPermission() {
+        // Clear all permission grants for test.
+        mContextFixture.addCallingOrSelfPermission("");
+        int srvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+        mTelephonyRegistry.notifySrvccStateChanged(0 /*subId*/, srvccState);
+        try {
+            mTelephonyRegistry.listenForSubscriber(0 /*subId*/, mContext.getOpPackageName(),
+                    mPhoneStateListener.callback,
+                    LISTEN_SRVCC_STATE_CHANGED, true);
+            fail();
+        } catch (SecurityException e) {
+            // pass test!
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 2cf880b..0642e31 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -43,6 +43,7 @@
 import android.os.ServiceManager;
 import android.provider.BlockedNumberContract;
 import android.provider.Settings;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -58,8 +59,8 @@
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
-import com.android.internal.telephony.dataconnection.AccessNetworksManager;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
@@ -191,7 +192,7 @@
     @Mock
     protected DeviceStateMonitor mDeviceStateMonitor;
     @Mock
-    protected AccessNetworksManager mAccessNetworksManager;
+    protected TransportManager mTransportManager;
     @Mock
     protected IntentBroadcaster mIntentBroadcaster;
     @Mock
@@ -347,7 +348,7 @@
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
                 .makeIccPhoneBookInterfaceManager(nullable(Phone.class));
         doReturn(mDcTracker).when(mTelephonyComponentFactory)
-                .makeDcTracker(nullable(Phone.class));
+                .makeDcTracker(nullable(Phone.class), anyInt());
         doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
                 .makeWspTypeDecoder(nullable(byte[].class));
         doReturn(mImsCT).when(mTelephonyComponentFactory)
@@ -368,12 +369,13 @@
                 .makeCarrierActionAgent(nullable(Phone.class));
         doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory)
                 .makeDeviceStateMonitor(nullable(Phone.class));
-        doReturn(mAccessNetworksManager).when(mTelephonyComponentFactory)
-                .makeAccessNetworksManager(nullable(Phone.class));
+        doReturn(mTransportManager).when(mTelephonyComponentFactory)
+                .makeTransportManager(nullable(Phone.class));
         doReturn(mNitzStateMachine).when(mTelephonyComponentFactory)
                 .makeNitzStateMachine(nullable(GsmCdmaPhone.class));
         doReturn(mLocaleTracker).when(mTelephonyComponentFactory)
-                .makeLocaleTracker(nullable(Phone.class), nullable(Looper.class));
+                .makeLocaleTracker(nullable(Phone.class), nullable(NitzStateMachine.class),
+                        nullable(Looper.class));
 
         //mPhone
         doReturn(mContext).when(mPhone).getContext();
@@ -383,6 +385,7 @@
         doReturn(mServiceState).when(mPhone).getServiceState();
         doReturn(mServiceState).when(mImsPhone).getServiceState();
         doReturn(mPhone).when(mImsPhone).getDefaultPhone();
+        doReturn(true).when(mDcTracker).isDataEnabled();
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mPhone).getPhoneType();
         doReturn(mCT).when(mPhone).getCallTracker();
@@ -391,6 +394,8 @@
         doReturn(mCarrierActionAgent).when(mPhone).getCarrierActionAgent();
         doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
         doReturn(mIccSmsInterfaceManager).when(mPhone).getIccSmsInterfaceManager();
+        doReturn(mTransportManager).when(mPhone).getTransportManager();
+        doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
         mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
         mPhone.mEriManager = mEriManager;
 
@@ -463,6 +468,9 @@
         mSST.mSS = mServiceState;
         mSST.mRestrictedState = mRestrictedState;
         mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
+        doReturn(new int[]{TransportType.WWAN, TransportType.WLAN})
+                .when(mTransportManager).getAvailableTransports();
+        doReturn(TransportType.WWAN).when(mTransportManager).getCurrentTransport(anyInt());
 
         //SIM
         doReturn(1).when(mTelephonyManager).getSimCount();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
index 81aed77..e2cf307 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TimeZoneLookupHelperTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index b9738e5..b7b8d83 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -120,7 +120,7 @@
         assertEquals(a1.getApnTypeBitmask(), a2.getApnTypeBitmask());
         assertEquals(a1.isEnabled(), a2.isEnabled());
         assertEquals(a1.getProfileId(), a2.getProfileId());
-        assertEquals(a1.getModemCognitive(), a2.getModemCognitive());
+        assertEquals(a1.isPersistent(), a2.isPersistent());
         assertEquals(a1.getMaxConns(), a2.getMaxConns());
         assertEquals(a1.getWaitTime(), a2.getWaitTime());
         assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
@@ -229,7 +229,17 @@
         expectedApn = ApnSetting.makeApnSetting(
                 -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
                 mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
-                0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3);
+                0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3, -1);
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+        // A v6 string with carrierId=100
+        testString =
+            "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,3,"
+                + "100";
+        expectedApn = ApnSetting.makeApnSetting(
+            -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+            mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+            0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3, 100);
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // Return no apn if insufficient fields given.
@@ -269,7 +279,7 @@
         expectedApns.add(ApnSetting.makeApnSetting(
                 -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
                 mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
-                0, 0, false, 0, 0, 0, 0, -1, "", 3));
+                0, 0, false, 0, 0, 0, 0, -1, "", 3, -1));
         assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
     }
 
@@ -292,7 +302,7 @@
                 99, "12345", "Name", "apn", null, 10,
                 null, null, -1, "user", "password", 0,
                 ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
-                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "", 3);
+                networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "", 3, -1);
         expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
                 + "null, null, null, 10, 0, default, "
                 + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 8192, 3";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 97fcc75..d657cf8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -110,7 +110,7 @@
 
     @SmallTest
     public void testCreateFromApnSetting() throws Exception {
-        DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+        DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId(), false);
         assertEquals(mApn1.getProfileId(), dp.getProfileId());
         assertEquals(mApn1.getApnName(), dp.getApn());
         assertEquals(ApnSetting.getProtocolStringFromInt(mApn1.getProtocol()), dp.getProtocol());
@@ -118,15 +118,15 @@
         assertEquals(mApn1.getUser(), dp.getUserName());
         assertEquals(mApn1.getPassword(), dp.getPassword());
         assertEquals(0, dp.getType());  // TYPE_COMMON
-        assertEquals(mApn1.getMaxConnsTime(), dp.getMaxConnsTime());
-        assertEquals(mApn1.getMaxConns(), dp.getMaxConns());
         assertEquals(mApn1.getWaitTime(), dp.getWaitTime());
         assertEquals(mApn1.isEnabled(), dp.isEnabled());
+        assertFalse(dp.isPersistent());
+        assertFalse(dp.isPreferred());
     }
 
     @SmallTest
     public void testCreateFromApnSettingWithNetworkTypeBitmask() throws Exception {
-        DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.getProfileId());
+        DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.getProfileId(), false);
         assertEquals(mApn3.getProfileId(), dp.getProfileId());
         assertEquals(mApn3.getApnName(), dp.getApn());
         assertEquals(ApnSetting.getProtocolStringFromInt(mApn3.getProtocol()), dp.getProtocol());
@@ -134,8 +134,6 @@
         assertEquals(mApn3.getUser(), dp.getUserName());
         assertEquals(mApn3.getPassword(), dp.getPassword());
         assertEquals(2, dp.getType());  // TYPE_3GPP2
-        assertEquals(mApn3.getMaxConnsTime(), dp.getMaxConnsTime());
-        assertEquals(mApn3.getMaxConns(), dp.getMaxConns());
         assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
         assertEquals(mApn3.isEnabled(), dp.isEnabled());
         int expectedBearerBitmap =
@@ -146,11 +144,11 @@
 
     @SmallTest
     public void testEquals() throws Exception {
-        DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
-        DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+        DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId(), false);
+        DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId(), false);
         assertEquals(dp1, dp2);
 
-        dp2 = DcTracker.createDataProfile(mApn2, mApn2.getProfileId());
+        dp2 = DcTracker.createDataProfile(mApn2, mApn2.getProfileId(), false);
         assertFalse(dp1.equals(dp2));
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index c86c911..400a9a6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -42,7 +42,6 @@
 
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
 import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
@@ -55,7 +54,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.List;
 
 public class DcControllerTest extends TelephonyTest {
 
@@ -63,11 +62,11 @@
     private static final int EVENT_DATA_STATE_CHANGED = 0x00040007;
 
     @Mock
-    DataConnection mDc;
+    private DataConnection mDc;
     @Mock
-    HashMap<ApnContext, ConnectionParams> mApnContexts;
+    private List<ApnContext> mApnContexts;
     @Mock
-    DataServiceManager mDataServiceManager;
+    private DataServiceManager mDataServiceManager;
 
     UpdateLinkPropertyResult mResult;
 
@@ -107,8 +106,8 @@
         super.setUp(getClass().getSimpleName());
 
         doReturn("fake.action_detached").when(mPhone).getActionDetached();
-        mDc.mApnContexts = mApnContexts;
         doReturn(1).when(mApnContexts).size();
+        doReturn(mApnContexts).when(mDc).getApnContexts();
 
         LinkProperties lp = new LinkProperties();
         mResult = new UpdateLinkPropertyResult(lp);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 8015315..a5025d1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -29,6 +29,7 @@
 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;
@@ -51,6 +52,7 @@
 import android.net.NetworkRequest;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
@@ -73,6 +75,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.LocalLog;
+import android.util.Pair;
 
 import com.android.internal.R;
 import com.android.internal.telephony.DctConstants;
@@ -128,6 +131,7 @@
             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;
 
     @Mock
     ISub mIsub;
@@ -145,6 +149,8 @@
     DataConnection mDataConnection;
     @Mock
     PackageManagerService mMockPackageManagerInternal;
+    @Mock
+    Handler mHandler;
 
     private DcTracker mDct;
     private DcTrackerTestHandler mDcTrackerTestHandler;
@@ -158,6 +164,8 @@
     private final ApnSettingContentProvider mApnSettingContentProvider =
             new ApnSettingContentProvider();
 
+    private Message mMessage;
+
     private void addDataService() {
         CellularDataService cellularDataService = new CellularDataService();
         ServiceInfo serviceInfo = new ServiceInfo();
@@ -233,7 +241,8 @@
                                     Telephony.Carriers.MVNO_TYPE,
                                     Telephony.Carriers.MVNO_MATCH_DATA,
                                     Telephony.Carriers.NETWORK_TYPE_BITMASK,
-                                    Telephony.Carriers.APN_SET_ID});
+                                    Telephony.Carriers.APN_SET_ID,
+                                    Telephony.Carriers.CARRIER_ID});
 
                     mc.addRow(new Object[]{
                             2163,                   // id
@@ -255,7 +264,7 @@
                             ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
-                            0,                      // modem_cognitive
+                            1,                      // modem_cognitive
                             0,                      // max_conns
                             0,                      // wait_time
                             0,                      // max_conns_time
@@ -263,7 +272,8 @@
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
                             NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
-                            0                       // apn_set_id
+                            0,                      // apn_set_id
+                            -1                      // carrier_id
                     });
 
                     mc.addRow(new Object[]{
@@ -286,7 +296,7 @@
                             ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
                             0,                      // bearer_bitmask
                             0,                      // profile_id
-                            0,                      // modem_cognitive
+                            1,                      // modem_cognitive
                             0,                      // max_conns
                             0,                      // wait_time
                             0,                      // max_conns_time
@@ -294,7 +304,8 @@
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
                             NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
-                            0                       // apn_set_id
+                            0,                      // apn_set_id
+                            -1                      // carrier_id
                     });
 
                     mc.addRow(new Object[]{
@@ -317,7 +328,7 @@
                             0,                      // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
-                            0,                      // modem_cognitive
+                            1,                      // modem_cognitive
                             0,                      // max_conns
                             0,                      // wait_time
                             0,                      // max_conns_time
@@ -325,7 +336,8 @@
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
                             0,                      // network_type_bitmask
-                            0                       // apn_set_id
+                            0,                      // apn_set_id
+                            -1                      // carrier_id
                     });
 
                     mc.addRow(new Object[]{
@@ -348,7 +360,7 @@
                             ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
-                            0,                      // modem_cognitive
+                            1,                      // modem_cognitive
                             0,                      // max_conns
                             0,                      // wait_time
                             0,                      // max_conns_time
@@ -356,7 +368,8 @@
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
                             NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
-                            0                       // apn_set_id
+                            0,                      // apn_set_id
+                            -1                      // carrier_id
                     });
 
                     mc.addRow(new Object[]{
@@ -379,7 +392,7 @@
                             0,                      // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
-                            0,                      // modem_cognitive
+                            1,                      // modem_cognitive
                             0,                      // max_conns
                             0,                      // wait_time
                             0,                      // max_conns_time
@@ -387,7 +400,8 @@
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
                             0,                      // network_type_bitmask
-                            0                       // apn_set_id
+                            0,                      // apn_set_id
+                            -1                      // carrier_id
                     });
 
                     return mc;
@@ -518,17 +532,14 @@
         assertEquals("", dp.getUserName());
         assertEquals("", dp.getPassword());
         assertEquals(type, dp.getType());
-        assertEquals(0, dp.getMaxConnsTime());
-        assertEquals(0, dp.getMaxConns());
         assertEquals(0, dp.getWaitTime());
         assertTrue(dp.isEnabled());
         assertEquals(supportedApnTypesBitmap, dp.getSupportedApnTypesBitmap());
         assertEquals("IP", dp.getRoamingProtocol());
         assertEquals(bearerBitmask, dp.getBearerBitmap());
         assertEquals(0, dp.getMtu());
-        assertEquals("", dp.getMvnoType());
-        assertEquals("", dp.getMvnoMatchData());
-        assertFalse(dp.isModemCognitive());
+        assertTrue(dp.isPersistent());
+        assertFalse(dp.isPreferred());
     }
 
     private void verifyDataConnected(final String apnSetting) {
@@ -1021,7 +1032,7 @@
         doReturn(true).when(mApnContext).isEnabled();
         doReturn(true).when(mApnContext).getDependencyMet();
         doReturn(true).when(mApnContext).isReady();
-        doReturn(true).when(mApnContext).hasNoRestrictedRequests(eq(true));
+        doReturn(false).when(mApnContext).hasRestrictedRequests(eq(true));
     }
 
     // Test the emergency APN setup.
@@ -1125,7 +1136,7 @@
     @SmallTest
     public void testTrySetupRestrictedDataDisabled() throws Exception {
         initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
-        doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+        doReturn(true).when(mApnContext).hasRestrictedRequests(eq(true));
 
         mDct.setUserDataEnabled(false);
 
@@ -1153,7 +1164,7 @@
     @SmallTest
     public void testTrySetupRestrictedRoamingDisabled() throws Exception {
         initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
-        doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+        doReturn(true).when(mApnContext).hasRestrictedRequests(eq(true));
 
         mDct.setUserDataEnabled(true);
         mDct.setDataRoamingEnabledByUser(false);
@@ -1173,8 +1184,7 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        // expect no restricted data connection
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
                 eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
                 any(Message.class));
     }
@@ -1446,7 +1456,6 @@
         assertTrue(mDct.isDataEnabled());
         assertTrue(mDct.isUserDataEnabled());
 
-
         mDct.setUserDataEnabled(false);
         waitForMs(200);
 
@@ -1456,6 +1465,8 @@
 
         // 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());
@@ -1464,10 +1475,57 @@
         // 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);
+    }
+
+    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);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index d99bbc1..0a11a5b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -16,6 +16,12 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
@@ -26,119 +32,131 @@
 import android.os.Looper;
 import android.support.test.filters.FlakyTest;
 import android.telephony.Rlog;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.ContextFixture;
+import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.RadioConfig;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.mocks.ConnectivityServiceMock;
-import com.android.internal.telephony.mocks.DcTrackerMock;
 import com.android.internal.telephony.mocks.PhoneSwitcherMock;
 import com.android.internal.telephony.mocks.SubscriptionControllerMock;
 import com.android.internal.telephony.mocks.SubscriptionMonitorMock;
 import com.android.internal.telephony.mocks.TelephonyRegistryMock;
 
-public class TelephonyNetworkFactoryTest extends AndroidTestCase {
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+
+public class TelephonyNetworkFactoryTest extends TelephonyTest {
     private final static String LOG_TAG = "TelephonyNetworkFactoryTest";
 
-    private void waitABit() {
-        try {
-            Thread.sleep(250);
-        } catch (Exception e) {}
-    }
+    @Mock
+    private RadioConfig mMockRadioConfig;
 
     private String mTestName = "";
 
+    private TelephonyRegistryMock mTelephonyRegistryMock;
+    private PhoneSwitcherMock mPhoneSwitcherMock;
+    private SubscriptionControllerMock mSubscriptionControllerMock;
+    private SubscriptionMonitorMock mSubscriptionMonitorMock;
+    private HandlerThread mHandlerThread;
+    private ConnectivityServiceMock mConnectivityServiceMock;
+    private Looper mLooper;
+    private final ArrayList<NetworkRequest> mNetworkRequestList = new ArrayList<>();
+
+    private TelephonyNetworkFactory mTelephonyNetworkFactoryUT;
+
     private void log(String str) {
         Rlog.d(LOG_TAG + " " + mTestName, str);
     }
 
-    private class TestSetup {
-        final TelephonyRegistryMock telephonyRegistryMock;
-        final PhoneSwitcherMock phoneSwitcherMock;
-        final SubscriptionControllerMock subscriptionControllerMock;
-        final SubscriptionMonitorMock subscriptionMonitorMock;
-        final HandlerThread handlerThread;
-        final ConnectivityServiceMock connectivityServiceMock;
-        final Looper looper;
-        DcTrackerMock dcTrackerMock;
-        final Context contextMock;
-        private Object mLock = new Object();
-        private static final int MAX_INIT_WAIT_MS = 30000; // 30 seconds
-
-        TestSetup(int numPhones) {
-            handlerThread = new HandlerThread("TelephonyNetworkFactoryTest") {
-                @Override
-                public void onLooperPrepared() {
-                    synchronized (mLock) {
-                        if (dcTrackerMock == null) dcTrackerMock = new DcTrackerMock();
-                        mLock.notifyAll();
-                    }
-                }
-            };
-            handlerThread.start();
-            // wait until dct created
-            synchronized (mLock) {
-                try {
-                    mLock.wait(MAX_INIT_WAIT_MS);
-                } catch (InterruptedException ie) {
-                }
-                if (dcTrackerMock == null) fail("failed to initialize dct");
-            }
-            looper = handlerThread.getLooper();
-
-            final ContextFixture contextFixture = new ContextFixture();
-            String[] networkConfigString = getContext().getResources().getStringArray(
-                    com.android.internal.R.array.networkAttributes);
-            contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
-                    networkConfigString);
-            contextMock = contextFixture.getTestDouble();
-
-            connectivityServiceMock = new ConnectivityServiceMock(contextMock);
-            ConnectivityManager cm =
-                    new ConnectivityManager(contextMock, connectivityServiceMock);
-            contextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
-
-            telephonyRegistryMock = new TelephonyRegistryMock();
-            phoneSwitcherMock = new PhoneSwitcherMock(numPhones, looper);
-            subscriptionControllerMock =
-                    new SubscriptionControllerMock(contextMock, telephonyRegistryMock, numPhones);
-            subscriptionMonitorMock = new SubscriptionMonitorMock(numPhones);
-        }
-
-        void die() {
-            connectivityServiceMock.die();
-            looper.quit();
-            handlerThread.quit();
-        }
-    }
-
-    private TelephonyNetworkFactory makeTnf(int phoneId, TestSetup ts) {
-        return new TelephonyNetworkFactory(ts.phoneSwitcherMock, ts.subscriptionControllerMock,
-                ts.subscriptionMonitorMock, ts.looper, ts.contextMock, phoneId, ts.dcTrackerMock);
-    }
-
-    private NetworkRequest makeSubSpecificDefaultRequest(TestSetup ts, int subId) {
+    private NetworkRequest makeSubSpecificDefaultRequest(int subId) {
         NetworkCapabilities netCap = (new NetworkCapabilities()).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
-        return ts.connectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
+        return mConnectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
-    private NetworkRequest makeSubSpecificMmsRequest(TestSetup ts, int subId) {
+    private NetworkRequest makeSubSpecificMmsRequest(int subId) {
         NetworkCapabilities netCap = (new NetworkCapabilities()).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
-        return ts.connectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
+        return mConnectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
 
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        replaceInstance(RadioConfig.class, "sRadioConfig", null, mMockRadioConfig);
+
+        mHandlerThread = new HandlerThread("TelephonyNetworkFactoryTest");
+        mHandlerThread.start();
+        mLooper = mHandlerThread.getLooper();
+
+        mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
+                new String[]{"wifi,1,1,1,-1,true", "mobile,0,0,0,-1,true",
+                        "mobile_mms,2,0,2,60000,true", "mobile_supl,3,0,2,60000,true",
+                        "mobile_dun,4,0,2,60000,true", "mobile_hipri,5,0,3,60000,true",
+                        "mobile_fota,10,0,2,60000,true", "mobile_ims,11,0,2,60000,true",
+                        "mobile_cbs,12,0,2,60000,true", "wifi_p2p,13,1,0,-1,true",
+                        "mobile_ia,14,0,2,-1,true", "mobile_emergency,15,0,2,-1,true"});
+
+        doAnswer(invocation -> {
+            mNetworkRequestList.add((NetworkRequest) invocation.getArguments()[0]);
+            return null;
+        }).when(mDcTracker).requestNetwork(any(), any());
+
+        doAnswer(invocation -> {
+            mNetworkRequestList.remove((NetworkRequest) invocation.getArguments()[0]);
+            return null;
+        }).when(mDcTracker).releaseNetwork(any(), any());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mConnectivityServiceMock.die();
+        mLooper.quit();
+        mHandlerThread.quit();
+        super.tearDown();
+    }
+
+    private void createMockedTelephonyComponents(int numberOfPhones) throws Exception {
+        mConnectivityServiceMock = new ConnectivityServiceMock(mContext);
+        mContextFixture.setSystemService(Context.CONNECTIVITY_SERVICE,
+                new ConnectivityManager(mContext, mConnectivityServiceMock));
+        mTelephonyRegistryMock = new TelephonyRegistryMock();
+        mPhoneSwitcherMock = new PhoneSwitcherMock(numberOfPhones, mLooper);
+        mSubscriptionControllerMock = new SubscriptionControllerMock(mContext,
+                mTelephonyRegistryMock, numberOfPhones);
+        mSubscriptionMonitorMock = new SubscriptionMonitorMock(numberOfPhones);
+        mPhoneSwitcherMock = new PhoneSwitcherMock(numberOfPhones, mLooper);
+        mSubscriptionControllerMock = new SubscriptionControllerMock(mContext,
+                mTelephonyRegistryMock, numberOfPhones);
+        mSubscriptionMonitorMock = new SubscriptionMonitorMock(numberOfPhones);
+
+        replaceInstance(SubscriptionController.class, "sInstance", null,
+                mSubscriptionControllerMock);
+        replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mPhoneSwitcherMock);
+
+        mTelephonyNetworkFactoryUT = new TelephonyNetworkFactory(mSubscriptionMonitorMock, mLooper,
+                mPhone);
+
+        replaceInstance(TelephonyNetworkFactory.class, "mDcTracker",
+                mTelephonyNetworkFactoryUT, mDcTracker);
+    }
 
     /**
      * Test that phone active changes cause the DcTracker to get poked.
      */
     @FlakyTest
+    @Test
     @SmallTest
     public void testActive() throws Exception {
         mTestName = "testActive";
@@ -146,91 +164,68 @@
         final int phoneId = 0;
         final int subId = 0;
 
-        TestSetup ts = new TestSetup(numberOfPhones);
+        createMockedTelephonyComponents(numberOfPhones);
 
-        makeTnf(phoneId, ts);
-
-        ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
-        ts.subscriptionControllerMock.setDefaultDataSubId(subId);
-        ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
-        ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
+        mPhoneSwitcherMock.setPreferredDataPhoneId(phoneId);
+        mSubscriptionControllerMock.setDefaultDataSubId(subId);
+        mSubscriptionControllerMock.setSlotSubId(phoneId, subId);
+        mSubscriptionMonitorMock.notifySubscriptionChanged(phoneId);
 
         log("addDefaultRequest");
-        ts.connectivityServiceMock.addDefaultRequest();
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("pretest of LiveRequests != 0");
-        }
+        mConnectivityServiceMock.addDefaultRequest();
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
         log("setPhoneActive true: phoneId = " + phoneId);
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
-            fail("post-active test of LiveRequests != 1");
-        }
+        mPhoneSwitcherMock.setPhoneActive(phoneId, true);
+        waitForMs(250);
+        assertEquals(1, mNetworkRequestList.size());
 
         log("makeSubSpecificDefaultRequest: subId = " + subId);
-        NetworkRequest subSpecificDefault = makeSubSpecificDefaultRequest(ts, subId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 2) {
-            fail("post-second-request test of LiveRequests != 2");
-        }
+        NetworkRequest subSpecificDefault = makeSubSpecificDefaultRequest(subId);
+        waitForMs(250);
+        assertEquals(2, mNetworkRequestList.size());
 
         log("setPhoneActive false: phoneId = " + phoneId);
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, false);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("post-inactive test of LiveRequests != 0");
-        }
+        mPhoneSwitcherMock.setPhoneActive(phoneId, false);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
         log("makeSubSpecificDefaultRequest: subId = " + subId);
-        NetworkRequest subSpecificMms = makeSubSpecificMmsRequest(ts, subId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("post-mms-add test of LiveRequests != 0");
-        }
+        NetworkRequest subSpecificMms = makeSubSpecificMmsRequest(subId);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
         log("setPhoneActive true: phoneId = " + phoneId);
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 3) {
-            fail("post-active-mms-add test of LiveRequests != 3");
-        }
+        mPhoneSwitcherMock.setPhoneActive(phoneId, true);
+        waitForMs(250);
+        assertEquals(3, mNetworkRequestList.size());
 
         log("releaseNetworkRequest: subSpecificDefault = " + subSpecificDefault);
-        ts.connectivityServiceMock.releaseNetworkRequest(subSpecificDefault);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 2) {
-            fail("post-remove-default test of LiveRequests != 2");
-        }
+        mConnectivityServiceMock.releaseNetworkRequest(subSpecificDefault);
+        waitForMs(250);
+        assertEquals(2, mNetworkRequestList.size());
 
         log("setPhoneActive false: phoneId = " + phoneId);
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, false);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 8, LiveRequests != 0");
-        }
+        mPhoneSwitcherMock.setPhoneActive(phoneId, false);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
         log("releaseNetworkRequest: subSpecificMms = " + subSpecificMms);
-        ts.connectivityServiceMock.releaseNetworkRequest(subSpecificMms);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 9, LiveRequests != 0");
-        }
+        mConnectivityServiceMock.releaseNetworkRequest(subSpecificMms);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
         log("setPhoneActive true: phoneId = " + phoneId);
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
-            fail("test 10, LiveRequests != 1," + ts.dcTrackerMock.getNumberOfLiveRequests());
-        }
-
-        ts.die();
+        mPhoneSwitcherMock.setPhoneActive(phoneId, true);
+        waitForMs(250);
+        assertEquals(1, mNetworkRequestList.size());
     }
 
     /**
      * Test that network request changes cause the DcTracker to get poked.
      */
+    @Test
     @SmallTest
     public void testRequests() throws Exception {
         mTestName = "testActive";
@@ -241,80 +236,57 @@
         final int altSubId = 1;
         final int unusedSubId = 2;
 
-        TestSetup ts = new TestSetup(numberOfPhones);
+        createMockedTelephonyComponents(numberOfPhones);
 
-        makeTnf(phoneId, ts);
+        mPhoneSwitcherMock.setPreferredDataPhoneId(phoneId);
+        mSubscriptionControllerMock.setDefaultDataSubId(subId);
+        mSubscriptionControllerMock.setSlotSubId(phoneId, subId);
+        mSubscriptionMonitorMock.notifySubscriptionChanged(phoneId);
+        waitForMs(250);
 
-        ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
-        ts.subscriptionControllerMock.setDefaultDataSubId(subId);
-        ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
-        ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
-        waitABit();
+        assertEquals(0, mNetworkRequestList.size());
 
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 1, LiveRequests != 0");
-        }
+        mPhoneSwitcherMock.setPhoneActive(phoneId, true);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
-        ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 2, LiveRequests != 0");
-        }
+        mConnectivityServiceMock.addDefaultRequest();
+        waitForMs(250);
+        assertEquals(1, mNetworkRequestList.size());
 
-        ts.connectivityServiceMock.addDefaultRequest();
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
-            fail("test 3, LiveRequests != 1");
-        }
+        mSubscriptionControllerMock.setSlotSubId(altPhoneId, altSubId);
+        waitForMs(250);
+        assertEquals(1, mNetworkRequestList.size());
 
-        ts.subscriptionControllerMock.setSlotSubId(altPhoneId, altSubId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
-            fail("test 4, LiveRequests != 1");
-        }
+        mPhoneSwitcherMock.setPreferredDataPhoneId(altPhoneId);
+        mSubscriptionControllerMock.setDefaultDataSubId(altSubId);
+        mPhoneSwitcherMock.notifyActivePhoneChange(phoneId);
 
-        ts.phoneSwitcherMock.setPreferredDataPhoneId(altPhoneId);
-        ts.subscriptionControllerMock.setDefaultDataSubId(altSubId);
-        ts.phoneSwitcherMock.notifyActivePhoneChange(phoneId);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 5, LiveRequests != 0");
-        }
+        makeSubSpecificMmsRequest(subId);
+        waitForMs(250);
+        assertEquals(1, mNetworkRequestList.size());
 
-        makeSubSpecificMmsRequest(ts, subId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
-            fail("test 6,  LiveRequests != 1");
-        }
+        mSubscriptionControllerMock.setSlotSubId(phoneId, unusedSubId);
+        mSubscriptionMonitorMock.notifySubscriptionChanged(phoneId);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
-        ts.subscriptionControllerMock.setSlotSubId(phoneId, unusedSubId);
-        ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 7,  LiveRequests != 0");
-        }
+        makeSubSpecificDefaultRequest(subId);
+        waitForMs(250);
+        assertEquals(0, mNetworkRequestList.size());
 
-        makeSubSpecificDefaultRequest(ts, subId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
-            fail("test 8, LiveRequests != 0");
-        }
+        mSubscriptionControllerMock.setSlotSubId(phoneId, subId);
+        mSubscriptionMonitorMock.notifySubscriptionChanged(phoneId);
+        waitForMs(250);
+        assertEquals(2, mNetworkRequestList.size());
 
-        ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
-        ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 2) {
-            fail("test 9,  LiveRequests != 2");
-        }
-
-        ts.subscriptionControllerMock.setDefaultDataSubId(subId);
-        ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
-        ts.phoneSwitcherMock.notifyActivePhoneChange(phoneId);
-        waitABit();
-        if (ts.dcTrackerMock.getNumberOfLiveRequests() != 3) {
-            fail("test 10, LiveRequests != 3");
-        }
-        ts.die();
+        mSubscriptionControllerMock.setDefaultDataSubId(subId);
+        mPhoneSwitcherMock.setPreferredDataPhoneId(phoneId);
+        mPhoneSwitcherMock.notifyActivePhoneChange(phoneId);
+        waitForMs(250);
+        assertEquals(3, mNetworkRequestList.size());
     }
 }
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 694d610..5e4cc44 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
@@ -29,23 +29,26 @@
         EmergencyNumber number = new EmergencyNumber(
                 "911",
                 "us",
-                // EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED
-                0,
-                // EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALLING
-                1);
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
         assertEquals(number.getNumber(), "911");
         assertEquals(number.getCountryIso(), "us");
         assertTrue(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
-        assertFalse(number.isInEmergencyServiceCategories(
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+        assertTrue(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
-        assertFalse(number.isInEmergencyServiceCategories(
+        assertTrue(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
         assertEquals(0, number.getEmergencyServiceCategoryBitmask());
 
         List<Integer> categories = number.getEmergencyServiceCategories();
@@ -57,7 +60,8 @@
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
-        assertEquals(1, number.getEmergencyNumberSourceBitmask());
+        assertEquals(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                number.getEmergencyNumberSourceBitmask());
 
         List<Integer> sources = number.getEmergencyNumberSources();
         assertEquals(1, sources.size());
@@ -69,24 +73,27 @@
         EmergencyNumber number = new EmergencyNumber(
                 "911",
                 "us",
-                // EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
-                8,
-                // EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALLING
-                // EMERGENCY_NUMBER_SOURCE_MODEM
-                5);
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
         assertEquals(number.getNumber(), "911");
         assertEquals(number.getCountryIso(), "us");
         assertFalse(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
         assertTrue(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
         assertFalse(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
         assertEquals(8, number.getEmergencyServiceCategoryBitmask());
 
         List<Integer> categories = number.getEmergencyServiceCategories();
@@ -98,7 +105,9 @@
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
         assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
-        assertEquals(5, number.getEmergencyNumberSourceBitmask());
+        assertEquals(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+                number.getEmergencyNumberSourceBitmask());
 
         List<Integer> sources = number.getEmergencyNumberSources();
         assertEquals(2, sources.size());
@@ -114,28 +123,34 @@
         EmergencyNumber number = new EmergencyNumber(
                 "110",
                 "jp",
-                // EMERGENCY_SERVICE_CATEGORY_POLICE
-                // EMERGENCY_SERVICE_CATEGORY_AMBULANCE
-                // EMERGENCY_SERVICE_CATEGORY_MIEC
-                35,
-                // EMERGENCY_NUMBER_SOURCE_NETWORK_SINGALING
-                // EMERGENCY_NUMBER_SOURCE_SIM
-                // EMERGENCY_NUMBER_SOURCE_DEFAULT
-                11);
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
         assertEquals(number.getNumber(), "110");
         assertEquals(number.getCountryIso(), "jp");
         assertFalse(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
-        assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
-        assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
         assertFalse(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
         assertFalse(number.isInEmergencyServiceCategories(
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
-        assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
-        assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
-        assertEquals(35, number.getEmergencyServiceCategoryBitmask());
+        assertTrue(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+        assertFalse(number.isInEmergencyServiceCategories(
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+        assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+                number.getEmergencyServiceCategoryBitmask());
 
         List<Integer> categories = number.getEmergencyServiceCategories();
         assertEquals(3, categories.size());
@@ -151,7 +166,10 @@
         assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
         assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
         assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
-        assertEquals(11, number.getEmergencyNumberSourceBitmask());
+        assertEquals(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM
+                | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT,
+                number.getEmergencyNumberSourceBitmask());
 
         List<Integer> sources = number.getEmergencyNumberSources();
         assertEquals(3, sources.size());
@@ -163,4 +181,34 @@
         Collections.sort(sourcesToVerify);
         assertTrue(sourcesToVerify.equals(sources));
     }
+
+    public void testEmergencyNumberDisplayPriority() throws Exception {
+        EmergencyNumber numberHighestDisplayPriority = new EmergencyNumber(
+                "911",
+                "us",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+
+        EmergencyNumber numberHigherDisplayPriority = new EmergencyNumber(
+                "922",
+                "us",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM);
+
+        EmergencyNumber numberLowestDisplayPriority = new EmergencyNumber(
+                "110",
+                "us",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+                        | EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
+
+        assertTrue(numberHighestDisplayPriority.compareTo(
+                numberHigherDisplayPriority) < 0);
+        assertTrue(numberHigherDisplayPriority.compareTo(
+                numberLowestDisplayPriority) < 0);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
index 2bf0094..fbb579e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -73,7 +73,7 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp("SubscriptionControllerTest");
+        super.setUp("ImsManagerTest");
         mPhoneId = mPhone.getPhoneId();
         mBundle = mContextFixture.getCarrierConfigBundle();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
new file mode 100644
index 0000000..4bb92c9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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.ims;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Looper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.MmTelFeatureConnection;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MmTelFeatureConnectionTest extends TelephonyTest {
+
+    private class TestCallback extends Binder implements IInterface {
+
+        @Override
+        public IBinder asBinder() {
+            return this;
+        }
+    }
+
+    private class CallbackManagerTest extends
+            MmTelFeatureConnection.CallbackAdapterManager<TestCallback> {
+
+        List<TestCallback> mCallbacks = new ArrayList<>();
+
+        CallbackManagerTest(Context context, Object lock) {
+            super(context, lock);
+        }
+
+        // A callback has been registered. Register that callback with the MmTelFeature.
+        @Override
+        public boolean registerCallback(TestCallback localCallback) {
+            return mCallbacks.add(localCallback);
+        }
+
+        // A callback has been removed, unregister that callback with the MmTelFeature.
+        @Override
+        public void unregisterCallback(TestCallback localCallback) {
+            mCallbacks.remove(localCallback);
+        }
+
+        public boolean doesCallbackExist(TestCallback callback) {
+            return mCallbacks.contains(callback);
+        }
+    }
+    private CallbackManagerTest mCallbackManagerUT;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp("MmTelFeatureConnectionTest");
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        mCallbackManagerUT = new CallbackManagerTest(mContext, this);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mCallbackManagerUT = null;
+        super.tearDown();
+    }
+
+    /**
+     * Basic test of deprecated functionality, ensure that adding the callback directly triggers the
+     * appropriate registerCallback and unregisterCallback calls.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_addAndRemoveCallback() throws Exception {
+        TestCallback testCallback = new TestCallback();
+        mCallbackManagerUT.addCallback(testCallback);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback));
+        // The subscriptions changed listener should only be added for callbacks that are being
+        // linked to a subscription.
+        verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        mCallbackManagerUT.removeCallback(testCallback);
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback));
+        // The subscriptions changed listener should only be removed for callbacks that are
+        // linked to a subscription.
+        verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * Ensure that adding the callback and linking subId triggers the appropriate registerCallback
+     * and unregisterCallback calls as well as the subscriptionChanged listener.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_addAndRemoveCallbackForSub() throws Exception {
+        TestCallback testCallback = new TestCallback();
+        int testSub = 1;
+        mCallbackManagerUT.addCallbackForSubscription(testCallback, testSub);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback));
+        verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        mCallbackManagerUT.removeCallbackForSubscription(testCallback, testSub);
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback));
+        verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * Ensure that adding the callback and linking multiple subIds trigger the appropriate
+     * registerCallback and unregisterCallback calls as well as the subscriptionChanged listener.
+     * When removing the callbacks, the subscriptionChanged listener shoud only be removed when all
+     * callbacks have been removed.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_addAndRemoveCallbackForMultipleSubs() throws Exception {
+        TestCallback testCallback1 = new TestCallback();
+        TestCallback testCallback2 = new TestCallback();
+        int testSub1 = 1;
+        int testSub2 = 2;
+        mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        // This should only happen once.
+        verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        mCallbackManagerUT.removeCallbackForSubscription(testCallback1, testSub1);
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        // removing the listener should not happen until the second callback is removed.
+        verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        mCallbackManagerUT.removeCallbackForSubscription(testCallback2, testSub2);
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * The subscriptions have changed, ensure that the callbacks registered to the original
+     * subscription testSub1 are removed, while keeping the callbacks for testSub2, since it was not
+     * removed.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_onSubscriptionsChangedMultipleSubs() throws Exception {
+        TestCallback testCallback1 = new TestCallback();
+        TestCallback testCallback2 = new TestCallback();
+        int testSub1 = 1;
+        int testSub2 = 2;
+        int testSub3 = 3;
+        mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        // Simulate subscriptions changed, where testSub1 is no longer active
+        doReturn(createSubscriptionInfoList(new int[] {testSub2, testSub3}))
+                .when(mSubscriptionManager).getActiveSubscriptionInfoList();
+        mCallbackManagerUT.mSubChangedListener.onSubscriptionsChanged();
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        // verify that the subscription changed listener is not removed, since we still have a
+        // callback on testSub2
+        verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * The active subscription has changed, ensure that the callback registered to the original
+     * subscription testSub1 are removed as well as the subscription changed listener, since
+     * there are mo more active callbacks.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_onSubscriptionsChangedOneSub() throws Exception {
+        TestCallback testCallback1 = new TestCallback();
+        int testSub1 = 1;
+        int testSub2 = 2;
+        mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        // Simulate subscriptions changed, where testSub1 is no longer active
+        doReturn(createSubscriptionInfoList(new int[] {testSub2}))
+                .when(mSubscriptionManager).getActiveSubscriptionInfoList();
+        mCallbackManagerUT.mSubChangedListener.onSubscriptionsChanged();
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        // verify that the subscription listener is removed, since the only active callback has been
+        // removed.
+        verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * The close() method has been called, so al callbacks should be cleaned up and notified
+     * that they have been removed. The subscriptions changed listener should also be removed.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_closeMultipleSubs() throws Exception {
+        TestCallback testCallback1 = new TestCallback();
+        TestCallback testCallback2 = new TestCallback();
+        int testSub1 = 1;
+        int testSub2 = 2;
+        mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        // Close the manager, ensure all subscription callbacks are removed
+        mCallbackManagerUT.close();
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        // verify that the subscription changed listener is removed.
+        verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    /**
+     * The close() method has been called, so all callbacks should be cleaned up. Since they are
+     * not associated with any subscriptions, no subscription based logic should be called.
+     */
+    @Test
+    @SmallTest
+    public void testCallbackAdapter_closeSlotBasedCallbacks() throws Exception {
+        TestCallback testCallback1 = new TestCallback();
+        TestCallback testCallback2 = new TestCallback();
+        mCallbackManagerUT.addCallback(testCallback1);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        mCallbackManagerUT.addCallback(testCallback2);
+        assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        // verify that the subscription changed listener is never called for these callbacks
+        // because they are not associated with any subscriptions.
+        verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+
+        // Close the manager, ensure all subscription callbacks are removed
+        mCallbackManagerUT.close();
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
+        assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
+        // verify that the subscription changed removed method is never called
+        verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
+                any(SubscriptionManager.OnSubscriptionsChangedListener.class));
+    }
+
+    private List<SubscriptionInfo> createSubscriptionInfoList(int[] subIds) {
+        List<SubscriptionInfo> infos = new ArrayList<>();
+        for (int i = 0; i < subIds.length; i++) {
+            SubscriptionInfo info = new SubscriptionInfo(subIds[i], null, -1, null, null, -1, -1,
+                    null, -1, null, null, null, null, false, null, null);
+            infos.add(info);
+        }
+        return infos;
+    }
+}
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 759309c..5728648 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -52,6 +52,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.feature.ImsFeature;
@@ -84,8 +85,8 @@
     private ImsPhoneCallTracker mCTUT;
     private ImsCTHandlerThread mImsCTHandlerThread;
     private MmTelFeature.Listener mMmTelListener;
-    private ImsRegistrationImplBase.Callback mRegistrationCallback;
-    private ImsFeature.CapabilityCallback mCapabilityCallback;
+    private ImsMmTelManager.RegistrationCallback mRegistrationCallback;
+    private ImsMmTelManager.CapabilityCallback mCapabilityCallback;
     private ImsCall.Listener mImsCallListener;
     private ImsCall mImsCall;
     private ImsCall mSecondImsCall;
@@ -114,6 +115,8 @@
                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
             mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.",
                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
+            mCTUT.addReasonCodeRemapping(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, "",
+                    ImsReasonInfo.CODE_SIP_FORBIDDEN);
             mCTUT.setDataEnabled(true);
             mCTHander = new Handler(mCTUT.getLooper());
             setReady(true);
@@ -215,13 +218,14 @@
         doAnswer(invocation -> {
             mRegistrationCallback = invocation.getArgument(0);
             return mRegistrationCallback;
-        }).when(mImsManager).addRegistrationCallback(any(ImsRegistrationImplBase.Callback.class));
+        }).when(mImsManager).addRegistrationCallback(
+                any(android.telephony.ims.ImsMmTelManager.RegistrationCallback.class));
 
         doAnswer(invocation -> {
-            mCapabilityCallback = (ImsFeature.CapabilityCallback) invocation.getArguments()[0];
+            mCapabilityCallback = (ImsMmTelManager.CapabilityCallback) invocation.getArguments()[0];
             return mCapabilityCallback;
 
-        }).when(mImsManager).addCapabilitiesCallback(any(ImsFeature.CapabilityCallback.class));
+        }).when(mImsManager).addCapabilitiesCallback(any(ImsMmTelManager.CapabilityCallback.class));
 
         doReturn(mImsConfig).when(mImsManager).getConfigInterface();
 
@@ -282,7 +286,7 @@
         assertFalse(mCTUT.isVowifiEnabled());
 
         // enable Voice over LTE
-        ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+        MmTelFeature.MmTelCapabilities caps = new MmTelFeature.MmTelCapabilities();
         caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         mCapabilityCallback.onCapabilitiesStatusChanged(caps);
         waitForHandlerAction(mCTHander, 1000);
@@ -300,7 +304,7 @@
         assertFalse(mCTUT.isVowifiEnabled());
 
         // enable Voice over IWLAN
-        ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+        MmTelFeature.MmTelCapabilities caps = new MmTelFeature.MmTelCapabilities();
         caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         mCapabilityCallback.onCapabilitiesStatusChanged(caps);
         waitForHandlerAction(mCTHander, 1000);
@@ -318,7 +322,7 @@
         assertFalse(mCTUT.isVideoCallEnabled());
 
         // enable only Voice
-        ImsFeature.Capabilities caps = new ImsFeature.Capabilities();
+        MmTelFeature.MmTelCapabilities caps = new MmTelFeature.MmTelCapabilities();
         caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         mCapabilityCallback.onCapabilitiesStatusChanged(caps);
         waitForHandlerAction(mCTHander, 1000);
@@ -330,7 +334,7 @@
         verify(mImsPhone, times(1)).onFeatureCapabilityChanged();
 
         // enable video call
-        ImsFeature.Capabilities capsVideo = new ImsFeature.Capabilities();
+        MmTelFeature.MmTelCapabilities capsVideo = new MmTelFeature.MmTelCapabilities();
         capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
         mCapabilityCallback.onCapabilitiesStatusChanged(capsVideo);
@@ -900,6 +904,15 @@
         Assert.fail("Expected CallStateException");
     }
 
+    @Test
+    @SmallTest
+    public void testNumericOnlyRemap() {
+        assertEquals(ImsReasonInfo.CODE_SIP_FORBIDDEN, mCTUT.maybeRemapReasonCode(
+                new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0)));
+        assertEquals(ImsReasonInfo.CODE_SIP_FORBIDDEN, mCTUT.maybeRemapReasonCode(
+                new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0, "")));
+    }
+
     private void placeCallAndMakeActive() {
         try {
             doAnswer(new Answer<ImsCall>() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
deleted file mode 100644
index c02f68b..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ /dev/null
@@ -1,170 +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.mocks;
-
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.os.Handler;
-import android.os.Message;
-import android.util.LocalLog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.dataconnection.DcTracker;
-
-import java.util.ArrayList;
-
-public class DcTrackerMock extends DcTracker {
-    public DcTrackerMock() {
-    }
-    @Override
-    public void registerServiceStateTrackerEvents() {
-        throw new RuntimeException("registerServiceStateTrackerEvents not implemented");
-    }
-    @Override
-    public void unregisterServiceStateTrackerEvents() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void setUserDataEnabled(boolean enable) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public long getSubId() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public DctConstants.Activity getActivity() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public LinkProperties getLinkProperties(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public NetworkCapabilities getNetworkCapabilities(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public String[] getActiveApnTypes() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public String getActiveApnString(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public DctConstants.State getState(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public DctConstants.State getOverallState() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean hasMatchedTetherApnSetting() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean getAutoAttachOnCreation() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean isUserDataEnabled() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean isDataEnabled() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void setDataRoamingEnabledByUser(boolean enabled) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean getDataRoamingEnabled() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean isDisconnected() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void update() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void cleanUpAllConnections(String cause) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void updateRecords() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void cleanUpAllConnections(String cause, Message disconnectAllCompleteMsg) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void unregisterForAllDataDisconnected(Handler h) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public boolean setInternalDataEnabled(boolean enable) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public String[] getPcscfAddress(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void sendStartNetStatPoll(DctConstants.Activity activity) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
-    public void sendStopNetStatPoll(DctConstants.Activity activity) {
-        throw new RuntimeException("Not Implemented");
-    }
-
-    private final ArrayList<NetworkRequest> mRequestedNetworks = new ArrayList<NetworkRequest>();
-
-    @Override
-    public void requestNetwork(NetworkRequest networkRequest, LocalLog log) {
-        synchronized (mRequestedNetworks) {
-            mRequestedNetworks.add(networkRequest);
-        }
-    }
-
-    @Override
-    public void releaseNetwork(NetworkRequest networkRequest, LocalLog log) {
-        synchronized (mRequestedNetworks) {
-            mRequestedNetworks.remove(networkRequest);
-        }
-    }
-
-    @VisibleForTesting
-    public int getNumberOfLiveRequests() {
-        synchronized (mRequestedNetworks) {
-            return mRequestedNetworks.size();
-        }
-    }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index 8e97481..d6c86c6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -20,7 +20,6 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -1025,7 +1024,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public void registerForAllDataDisconnected(Handler h, int what, Object obj) {
+    public void registerForAllDataDisconnected(Handler h, int what) {
         throw new RuntimeException("not implemented");
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
index 7f995f9..d3ab208 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
@@ -28,43 +28,45 @@
 
 public class PhoneSwitcherMock extends PhoneSwitcher {
     private final int mNumPhones;
-    private final RegistrantList mActivePhoneRegistrants[];
+    private final RegistrantList mActivePhoneRegistrants;
     private final AtomicBoolean mIsActive[];
 
     public PhoneSwitcherMock(int numPhones, Looper looper) {
         super(looper);
 
         mNumPhones = numPhones;
-        mActivePhoneRegistrants = new RegistrantList[numPhones];
+        mActivePhoneRegistrants = new RegistrantList();
         mIsActive = new AtomicBoolean[numPhones];
         for(int i = 0; i < numPhones; i++) {
-            mActivePhoneRegistrants[i] = new RegistrantList();
             mIsActive[i] = new AtomicBoolean(false);
         }
     }
 
     @Override
-    public void resendDataAllowed(int phoneId) {
+    public void onRadioCapChanged(int phoneId) {
         throw new RuntimeException("resendPhone not implemented");
     }
 
     @Override
-    public boolean isPhoneActive(int phoneId) {
+    public boolean shouldApplySpecifiedRequests(int phoneId) {
         return mIsActive[phoneId].get();
     }
 
     @Override
-    public void registerForActivePhoneSwitch(int phoneId, Handler h, int what, Object o) {
-        validatePhoneId(phoneId);
+    public boolean shouldApplyUnspecifiedRequests(int phoneId) {
+        return mIsActive[phoneId].get() && phoneId == mPreferredDataPhoneId;
+    }
+
+    @Override
+    public void registerForActivePhoneSwitch(Handler h, int what, Object o) {
         Registrant r = new Registrant(h, what, o);
-        mActivePhoneRegistrants[phoneId].add(r);
+        mActivePhoneRegistrants.add(r);
         r.notifyRegistrant();
     }
 
     @Override
-    public void unregisterForActivePhoneSwitch(int phoneId, Handler h) {
-        validatePhoneId(phoneId);
-        mActivePhoneRegistrants[phoneId].remove(h);
+    public void unregisterForActivePhoneSwitch(Handler h) {
+        mActivePhoneRegistrants.remove(h);
     }
 
     private void validatePhoneId(int phoneId) {
@@ -86,6 +88,6 @@
     }
 
     public void notifyActivePhoneChange(int phoneId) {
-        mActivePhoneRegistrants[phoneId].notifyRegistrants();
+        mActivePhoneRegistrants.notifyRegistrants();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
index 24f8cbc..df5552a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
@@ -28,7 +28,6 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
-import android.telephony.VoLteServiceState;
 
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
 import com.android.internal.telephony.IPhoneStateListener;
@@ -384,7 +383,7 @@
     }
 
     @Override
-    public void notifyVoLteServiceStateChanged(VoLteServiceState lteState) {
+    public void notifySrvccStateChanged(int subId, int state) {
         throw new RuntimeException("Not implemented");
     }
 
@@ -412,4 +411,14 @@
     public void notifyPhoneCapabilityChanged(PhoneCapability capability) {
         throw new RuntimeException("Not implemented");
     }
+
+    @Override
+    public void notifyRadioPowerStateChanged(int state) {
+        throw new RuntimeException("Not implemented");
+    }
+
+    @Override
+    public void notifyPreferredDataSubIdChanged(int subId) {
+        throw new RuntimeException("Not implemented");
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/rcs/RcsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/rcs/RcsControllerTest.java
new file mode 100644
index 0000000..d80b94b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/rcs/RcsControllerTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.rcs;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.internal.telephony.RcsController;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RcsControllerTest extends TelephonyTest {
+
+    private RcsController mRcsController;
+
+    @Before
+    public void setUp() {
+        mRcsController = new RcsController(mContext, null);
+    }
+
+    /**
+     * TODO(sahinc): fix the test once there is an implementation in place
+     */
+    @Test
+    public void testGetMessageCount() {
+        assertEquals(1018, mRcsController.getMessageCount(0));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 756015d..4012e76 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -26,10 +26,13 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.TelephonyTest;
@@ -40,15 +43,23 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.ArrayList;
+
 public class UiccControllerTest extends TelephonyTest {
     private UiccController mUiccControllerUT;
     private UiccControllerHandlerThread mUiccControllerHandlerThread;
     private static final int PHONE_COUNT = 1;
-    private static int ICC_CHANGED_EVENT = 0;
+    private static final int ICC_CHANGED_EVENT = 0;
+    private static final int EVENT_GET_ICC_STATUS_DONE = 3;
+    private static final int EVENT_GET_SLOT_STATUS_DONE = 4;
     @Mock
     private Handler mMockedHandler;
     @Mock
     private IccCardStatus mIccCardStatus;
+    @Mock
+    private UiccSlot mMockSlot;
+    @Mock
+    private UiccCard mMockCard;
 
     private class UiccControllerHandlerThread extends HandlerThread {
 
@@ -143,16 +154,15 @@
         mSimulatedCommands.requestShutdown(null);
         waitForMs(50);
         assertNull(mUiccControllerUT.getUiccCard(0));
-        assertEquals(CommandsInterface.RadioState.RADIO_UNAVAILABLE,
-                mSimulatedCommands.getRadioState());
+        assertEquals(TelephonyManager.RADIO_POWER_UNAVAILABLE, mSimulatedCommands.getRadioState());
     }
 
-    @Test@SmallTest
+    @Test @SmallTest
     public void testPowerOn() {
         mSimulatedCommands.setRadioPower(true, null);
         waitForMs(500);
         assertNotNull(mUiccControllerUT.getUiccCard(0));
-        assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState());
+        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
     }
 
     @Test @SmallTest
@@ -200,4 +210,53 @@
                 mCaptorLong.capture());
         assertEquals(ICC_CHANGED_EVENT, mCaptorMessage.getValue().what);
     }
+
+    @Test
+    public void testCardIdFromIccStatus() {
+        // Give UiccController a real context so it can use shared preferences
+        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
+
+        // Mock out UiccSlots
+        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        doReturn(mMockCard).when(mMockSlot).getUiccCard();
+        doReturn("A1B2C3D4").when(mMockCard).getCardId();
+        doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mMockCard).getCardState();
+
+        // simulate card status loaded
+        IccCardStatus ics = new IccCardStatus();
+        ics.setCardState(1 /* present */);
+        ics.setUniversalPinState(3 /* disabled */);
+        ics.atr = "abcdef0123456789abcdef";
+        ics.iccid = "123451234567890";
+        ics.eid = "A1B2C3D4";
+        AsyncResult ar = new AsyncResult(null, ics, null);
+        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_ICC_STATUS_DONE, ar);
+        mUiccControllerUT.handleMessage(msg);
+
+        // assert that the card ID was created
+        assertEquals(0, mUiccControllerUT.convertToPublicCardId(ics.eid));
+    }
+
+    @Test
+    public void testCardIdFromSlotStatus() {
+        // Give UiccController a real context so it can use shared preferences
+        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
+
+        // Mock out UiccSlots
+        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        doReturn(true).when(mMockSlot).isEuicc();
+
+        // simulate slot status loaded
+        IccSlotStatus iss = new IccSlotStatus();
+        iss.setSlotState(1 /* active */);
+        iss.eid = "ABADACB";
+        ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>();
+        status.add(iss);
+        AsyncResult ar = new AsyncResult(null, status, null);
+        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar);
+        mUiccControllerUT.handleMessage(msg);
+
+        // assert that the card ID was created
+        assertEquals(0, mUiccControllerUT.convertToPublicCardId(iss.eid));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index b76f2cf..e99a4e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -551,7 +552,8 @@
     @SmallTest
     public void testUpdateUiccProfileApplicationCdmaSupported() {
         // CDMA supported
-        doReturn(true).when(mUiccController).isCdmaSupported();
+        doReturn(true)
+            .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
 
         testWithCsimApp();
 
@@ -562,8 +564,9 @@
     @Test
     @SmallTest
     public void testUpdateUiccProfileApplicationCdmaNotSupported() {
-        // CDMA supported
-        doReturn(false).when(mUiccController).isCdmaSupported();
+        // CDMA not supported
+        doReturn(false)
+            .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
 
         testWithCsimApp();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index 40e113a..e782f1e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -166,6 +166,28 @@
     }
 
     @Test
+    public void testPassEidInContructor() throws InterruptedException {
+        mMockIccCardStatus.eid = "1A2B3C4D";
+        mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
+                mMockIccCardStatus, 0 /* phoneId */, new Object());
+
+        final int eventEidReady = 0;
+        final CountDownLatch latch = new CountDownLatch(1);
+        Handler handler = new Handler(mTestHandlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == eventEidReady) {
+                    assertEquals("1A2B3C4D", mEuiccCard.getEid());
+                    latch.countDown();
+                }
+            }
+        };
+        // This will instantly return, since EID is already set
+        mEuiccCard.registerForEidReady(handler, eventEidReady, null /* obj */);
+        assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
     public void testLoadEidAndNotifyRegistrants() throws InterruptedException {
         int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");