Merge "Revert "Hide system apps until installed (2/2)"" into pi-dev
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 5b53bcf..1513a33 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -522,6 +522,7 @@
             DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
             request.setAllowedOverMetered(false);
             request.setVisibleInDownloadsUi(false);
+            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
             Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
             SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
 
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index f022e65..7f49f28 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -30,6 +31,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -38,6 +41,7 @@
 import java.util.Map;
 
 
+
 /**
  * This contains Carrier specific logic based on the states/events
  * managed in ServiceStateTracker.
@@ -53,18 +57,70 @@
     private static final int UNINITIALIZED_DELAY_VALUE = -1;
     private Phone mPhone;
     private ServiceStateTracker mSST;
-
+    private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
+    private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     public static final int NOTIFICATION_PREF_NETWORK = 1000;
     public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;
 
-    private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
-
     public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
         this.mPhone = phone;
         this.mSST = sst;
         phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        // Listen for subscriber changes
+        SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
+                new OnSubscriptionsChangedListener(this.getLooper()) {
+                    @Override
+                    public void onSubscriptionsChanged() {
+                        int subId = mPhone.getSubId();
+                        if (mPreviousSubId != subId) {
+                            mPreviousSubId = subId;
+                            registerPrefNetworkModeObserver();
+                        }
+                    }
+                });
+
         registerNotificationTypes();
+        registerPrefNetworkModeObserver();
+    }
+
+    private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) {
+        @Override
+        public void onChange(boolean selfChange) {
+            handlePrefNetworkModeChanged();
+        }
+    };
+
+    /**
+     * Return preferred network mode observer
+     */
+    @VisibleForTesting
+    public ContentObserver getContentObserver() {
+        return mPrefNetworkModeObserver;
+    }
+
+    private void registerPrefNetworkModeObserver() {
+        int subId = mPhone.getSubId();
+        unregisterPrefNetworkModeObserver();
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            mPhone.getContext().getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId),
+                    true,
+                    mPrefNetworkModeObserver);
+        }
+    }
+
+    private void unregisterPrefNetworkModeObserver() {
+        mPhone.getContext().getContentResolver().unregisterContentObserver(
+                mPrefNetworkModeObserver);
+    }
+
+    /**
+     * Returns mNotificationTypeMap
+     */
+    @VisibleForTesting
+    public Map<Integer, NotificationType> getNotificationTypeMap() {
+        return mNotificationTypeMap;
     }
 
     private void registerNotificationTypes() {
@@ -152,14 +208,25 @@
     private void handleConfigChanges() {
         for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
             NotificationType notificationType = entry.getValue();
-            if (evaluateSendingMessage(notificationType)) {
-                Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
-                Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
-                sendMessageDelayed(notificationMsg, getDelay(notificationType));
-            } else {
-                cancelNotification(notificationType.getTypeId());
-                Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
-            }
+            evaluateSendingMessageOrCancelNotification(notificationType);
+        }
+    }
+
+    private void handlePrefNetworkModeChanged() {
+        NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
+        if (notificationType != null) {
+            evaluateSendingMessageOrCancelNotification(notificationType);
+        }
+    }
+
+    private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) {
+        if (evaluateSendingMessage(notificationType)) {
+            Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
+            Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
+            sendMessageDelayed(notificationMsg, getDelay(notificationType));
+        } else {
+            cancelNotification(notificationType.getTypeId());
+            Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
         }
     }
 
@@ -241,6 +308,13 @@
     }
 
     /**
+     * Dispose the CarrierServiceStateTracker.
+     */
+    public void dispose() {
+        unregisterPrefNetworkModeObserver();
+    }
+
+    /**
      * Class that defines the different types of notifications.
      */
     public interface NotificationType {
@@ -294,7 +368,7 @@
             }
             this.mDelay = bundle.getInt(
                     CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
-            Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+            Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay);
         }
 
         public int getDelay() {
@@ -312,7 +386,7 @@
             Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
                     + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
                     + "," + mSST.isRadioOn());
-            if (mDelay == UNINITIALIZED_DELAY_VALUE ||  isPhoneStillRegistered() || isGlobalMode()
+            if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
                     || isRadioOffOrAirplaneMode()) {
                 return false;
             }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 3055e90..2c167bd 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -29,6 +29,7 @@
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
@@ -1510,15 +1511,20 @@
     }
 
     @Override
+    @Nullable
     public String getSubscriberId() {
-        if (isPhoneTypeGsm()) {
-            IccRecords r = mIccRecords.get();
-            return (r != null) ? r.getIMSI() : null;
-        } else if (isPhoneTypeCdma()) {
-            return mSST.getImsi();
-        } else { //isPhoneTypeCdmaLte()
-            return (mSimRecords != null) ? mSimRecords.getIMSI() : "";
+        String subscriberId = null;
+        if (isPhoneTypeCdma()) {
+            subscriberId = mSST.getImsi();
+        } else {
+            // Both Gsm and CdmaLte get the IMSI from Usim.
+            IccRecords iccRecords = mUiccController.getIccRecords(
+                    mPhoneId, UiccController.APP_FAM_3GPP);
+            if (iccRecords != null) {
+                subscriberId = iccRecords.getIMSI();
+            }
         }
+        return subscriberId;
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index e7bd046..79ea8d2 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -280,6 +280,10 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " SS=" + mPhone.getServiceState().getState());
 
+        // Flag that this Tracker is using the ImsService implementation of SMS over IMS for sending
+        // this message. Any fallbacks will happen over CS only.
+        tracker.mUsesImsServiceForIms = true;
+
         HashMap<String, Object> map = tracker.getData();
 
         byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index c2b576b..996e288 100644
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import android.annotation.NonNull;
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -34,6 +35,7 @@
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -55,8 +57,14 @@
     private static final boolean DBG = true;
     private static final String TAG = LocaleTracker.class.getSimpleName();
 
-    /** Event to trigger get cell info from the modem */
-    private static final int EVENT_GET_CELL_INFO = 1;
+    /** Event for getting cell info from the modem */
+    private static final int EVENT_GET_CELL_INFO                = 1;
+
+    /** Event for operator numeric update */
+    private static final int EVENT_UPDATE_OPERATOR_NUMERIC      = 2;
+
+    /** Event for service state changed */
+    private static final int EVENT_SERVICE_STATE_CHANGED        = 3;
 
     // Todo: Read this from Settings.
     /** The minimum delay to get cell info from the modem */
@@ -64,7 +72,11 @@
 
     // Todo: Read this from Settings.
     /** The maximum delay to get cell info from the modem */
-    private static final long CELL_INFO_MAX_DELAY_MS = 1 * HOUR_IN_MILLIS;
+    private static final long CELL_INFO_MAX_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+    // Todo: Read this from Settings.
+    /** The delay for periodically getting cell info from the modem */
+    private static final long CELL_INFO_PERIODIC_POLLING_DELAY_MS = 10 * MINUTE_IN_MILLIS;
 
     private final Phone mPhone;
 
@@ -86,6 +98,9 @@
     @Nullable
     private String mCurrentCountryIso;
 
+    /** Current service state. Must be one of ServiceState.STATE_XXX. */
+    private int mLastServiceState = -1;
+
     private final LocalLog mLocalLog = new LocalLog(50);
 
     /** Broadcast receiver to get SIM card state changed event */
@@ -116,6 +131,13 @@
                     updateLocale();
                 }
                 break;
+            case EVENT_UPDATE_OPERATOR_NUMERIC:
+                updateOperatorNumericSync((String) msg.obj);
+                break;
+            case EVENT_SERVICE_STATE_CHANGED:
+                AsyncResult ar = (AsyncResult) msg.obj;
+                onServiceStateChanged((ServiceState) ar.result);
+                break;
             default:
                 throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
         }
@@ -135,6 +157,8 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
         mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
+
+        mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
     }
 
     /**
@@ -202,19 +226,46 @@
     }
 
     /**
-     * Update MCC/MNC from network service state.
+     * Called when service state changed.
+     *
+     * @param serviceState Service state
+     */
+    private void onServiceStateChanged(ServiceState serviceState) {
+        int state = serviceState.getState();
+        if (state != mLastServiceState) {
+            if (state != ServiceState.STATE_POWER_OFF && TextUtils.isEmpty(mOperatorNumeric)) {
+                // When the device is out of airplane mode or powered on, and network's MCC/MNC is
+                // not available, we get cell info from the modem.
+                String msg = "Service state " + ServiceState.rilServiceStateToString(state)
+                        + ". Get cell info now.";
+                if (DBG) log(msg);
+                mLocalLog.log(msg);
+                getCellInfo();
+            } else if (state == ServiceState.STATE_POWER_OFF) {
+                // Clear the cell info when the device is in airplane mode.
+                if (mCellInfo != null) mCellInfo.clear();
+                stopCellInfoRetry();
+            }
+            updateLocale();
+            mLastServiceState = state;
+        }
+    }
+
+    /**
+     * Update MCC/MNC from network service state synchronously. Note if this is called from phone
+     * process's main thread and if the update operation requires getting cell info from the modem,
+     * the cached cell info will be used to determine the locale. If the cached cell info is not
+     * acceptable, use {@link #updateOperatorNumericAsync(String)} instead.
      *
      * @param operatorNumeric MCC/MNC of the operator
      */
-    public synchronized void updateOperatorNumeric(String operatorNumeric) {
+    public synchronized void updateOperatorNumericSync(String operatorNumeric) {
         // Check if the operator numeric changes.
-        String msg = "updateOperatorNumeric. mcc/mnc=" + operatorNumeric;
-        if (DBG) log(msg);
-        mLocalLog.log(msg);
+        if (DBG) log("updateOperatorNumericSync. mcc/mnc=" + operatorNumeric);
         if (!Objects.equals(mOperatorNumeric, operatorNumeric)) {
-            if (DBG) {
-                log("onUpdateOperatorNumeric: operator numeric changes to " + operatorNumeric);
-            }
+            String msg = "Operator numeric changes to " + operatorNumeric;
+            if (DBG) log(msg);
+            mLocalLog.log(msg);
             mOperatorNumeric = operatorNumeric;
 
             // If the operator numeric becomes unavailable, we need to get the latest cell info so
@@ -224,12 +275,30 @@
                     log("Operator numeric unavailable. Get latest cell info from the modem.");
                 }
                 getCellInfo();
+            } else {
+                // If operator numeric is available, that means we camp on network. So we should
+                // clear the cell info and stop cell info retry.
+                if (mCellInfo != null) mCellInfo.clear();
+                stopCellInfoRetry();
             }
             updateLocale();
         }
     }
 
     /**
+     * Update MCC/MNC from network service state asynchronously. The update operation will run
+     * in locale tracker's handler's thread, which can get cell info synchronously from service
+     * state tracker. Note that the country code will not be available immediately after calling
+     * this method.
+     *
+     * @param operatorNumeric MCC/MNC of the operator
+     */
+    public void updateOperatorNumericAsync(String operatorNumeric) {
+        if (DBG) log("updateOperatorNumericAsync. mcc/mnc=" + operatorNumeric);
+        sendMessage(obtainMessage(EVENT_UPDATE_OPERATOR_NUMERIC, operatorNumeric));
+    }
+
+    /**
      * Get the delay time to get cell info from modem. The delay time grows exponentially to prevent
      * battery draining.
      *
@@ -248,25 +317,48 @@
     }
 
     /**
+     * Stop retrying getting cell info from the modem. It cancels any scheduled cell info retrieving
+     * request.
+     */
+    private void stopCellInfoRetry() {
+        mFailCellInfoCount = 0;
+        removeMessages(EVENT_GET_CELL_INFO);
+    }
+
+    /**
      * Get cell info from the modem.
      */
     private void getCellInfo() {
+        String msg;
+        if (!mPhone.getServiceStateTracker().getDesiredPowerState()) {
+            msg = "Radio is off. Stopped cell info retry. Cleared the previous cached cell info.";
+            if (mCellInfo != null) mCellInfo.clear();
+            if (DBG) log(msg);
+            mLocalLog.log(msg);
+            stopCellInfoRetry();
+            return;
+        }
+
         // Get all cell info. Passing null to use default worksource, which indicates the original
         // request is from telephony internally.
         mCellInfo = mPhone.getAllCellInfo(null);
-        String msg = "getCellInfo: cell info=" + mCellInfo;
+        msg = "getCellInfo: cell info=" + mCellInfo;
         if (DBG) log(msg);
         mLocalLog.log(msg);
         if (CollectionUtils.isEmpty(mCellInfo)) {
             // If we can't get a valid cell info. Try it again later.
             long delay = getCellInfoDelayTime(++mFailCellInfoCount);
             if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs.");
+            removeMessages(EVENT_GET_CELL_INFO);
             sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay);
         } else {
-            mFailCellInfoCount = 0;
-            // We successfully got cell info from the modem. Cancel the queued get cell info event
-            // if there is any.
-            removeMessages(EVENT_GET_CELL_INFO);
+            // We successfully got cell info from the modem. We should stop cell info retry.
+            stopCellInfoRetry();
+
+            // Now we need to get the cell info from the modem periodically even if we already got
+            // the cell info because the user can move.
+            sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO),
+                    CELL_INFO_PERIODIC_POLLING_DELAY_MS);
         }
     }
 
@@ -276,7 +368,7 @@
     private void updateLocale() {
         // If MCC is available from network service state, use it first.
         String mcc = null;
-        String countryIso = null;
+        String countryIso = "";
         if (!TextUtils.isEmpty(mOperatorNumeric)) {
             try {
                 mcc = mOperatorNumeric.substring(0, 3);
@@ -305,7 +397,9 @@
         log(msg);
         mLocalLog.log(msg);
         if (!Objects.equals(countryIso, mCurrentCountryIso)) {
-            log("updateLocale: Change the current country to " + countryIso);
+            msg = "updateLocale: Change the current country to " + countryIso;
+            log(msg);
+            mLocalLog.log(msg);
             mCurrentCountryIso = countryIso;
 
             TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(),
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 66405be..0f431ef 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -1420,8 +1420,6 @@
      */
     public void registerForServiceStateChanged(
             Handler h, int what, Object obj) {
-        checkCorrectThread(h);
-
         mServiceStateRegistrants.add(h, what, obj);
     }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 374c639..9e74ee2 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -293,11 +293,6 @@
                             " mRadioProxyCookie = " + mRadioProxyCookie.get());
                     if ((long) msg.obj == mRadioProxyCookie.get()) {
                         resetProxyAndRequestList();
-
-                        // todo: rild should be back up since message was sent with a delay. this is
-                        // a hack.
-                        getRadioProxy(null);
-                        getOemHookProxy(null);
                     }
                     break;
             }
@@ -350,6 +345,7 @@
         clearRequestList(RADIO_NOT_AVAILABLE, false);
 
         getRadioProxy(null);
+        getOemHookProxy(null);
     }
 
     /** Returns a {@link IRadio} instance or null if the service is not available. */
@@ -416,7 +412,7 @@
 
         try {
             mOemHookProxy = IOemHook.getService(
-                    HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+                    HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId], true);
             if (mOemHookProxy != null) {
                 // not calling linkToDeath() as ril service runs in the same process and death
                 // notification for that should be sufficient
@@ -435,12 +431,6 @@
                         CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
                 result.sendToTarget();
             }
-
-            // if service is not up, treat it like death notification to try to get service again
-            mRilHandler.sendMessageDelayed(
-                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
-                            mRadioProxyCookie.incrementAndGet()),
-                    IRADIO_GET_SERVICE_DELAY_MILLIS);
         }
 
         return mOemHookProxy;
@@ -1199,18 +1189,27 @@
             try {
                 if (radioProxy12 == null) {
                     // IRadio V1.0
+
+                    // 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) {
+                        ServiceState ss = phone.getServiceState();
+                        if (ss != null) {
+                            dataRat = ss.getRilDataRadioTechnology();
+                        }
+                    }
                     if (RILJ_LOGD) {
                         riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                                + ",radioTechnology=unknown,isRoaming=" + isRoaming
+                                + ",dataRat=" + dataRat + ",isRoaming=" + isRoaming
                                 + ",allowRoaming=" + allowRoaming + "," + dataProfile);
                     }
-                    // The RAT field in setup data call request was never used before. Starting from
-                    // P, the new API passes in access network type instead of RAT. Since it's
-                    // not possible to convert access network type back to RAT, but we still need to
-                    // support the 1.0 API, we passed in unknown RAT to the modem. And modem must
-                    // setup the data call on its current camped network.
-                    radioProxy.setupDataCall(rr.mSerial, ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN,
-                            dpi, dataProfile.isModemCognitive(), allowRoaming, isRoaming);
+
+                    radioProxy.setupDataCall(rr.mSerial, dataRat, dpi,
+                            dataProfile.isModemCognitive(), allowRoaming, isRoaming);
                 } else {
                     // IRadio V1.2
                     ArrayList<String> addresses = new ArrayList<>();
@@ -2011,6 +2010,10 @@
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestRaw", e);
             }
+        } else {
+            // OEM Hook service is disabled for P and later devices.
+            // Deprecated OEM Hook APIs will perform dummy before being removed.
+            if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
         }
     }
 
@@ -2036,6 +2039,10 @@
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
             }
+        } else {
+            // OEM Hook service is disabled for P and later devices.
+            // Deprecated OEM Hook APIs will perform dummy before being removed.
+            if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
         }
     }
 
@@ -5025,6 +5032,8 @@
                 return "RIL_UNSOL_ICC_SLOT_STATUS";
             case RIL_UNSOL_KEEPALIVE_STATUS:
                 return "RIL_UNSOL_KEEPALIVE_STATUS";
+            case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
+                return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
             default:
                 return "<unknown response>";
         }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index e80e5e7..f7a7943 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -36,6 +36,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESEND_INCALL_MUTE;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED;
@@ -295,6 +296,8 @@
             response.add(new PhysicalChannelConfig(status, config.cellBandwidthDownlink));
         }
 
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+
         mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
     }
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index b082972..2b4b5e9 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -101,7 +101,7 @@
 
     /** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
     public void ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange) {
-        if (isSameRatFamily(oldSS, newSS)) {
+        if (!locationChange && isSameRatFamily(oldSS, newSS)) {
             updateBandwidths(oldSS.getCellBandwidths(), newSS);
         }
         // temporarily disable rat ratchet on location change.
@@ -123,9 +123,9 @@
             int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
                     newSS.getRilDataRadioTechnology());
             newSS.setRilDataRadioTechnology(newDataRat);
-        } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+        } else if (oldSS.getRilDataRadioTechnology() != newSS.getRilDataRadioTechnology()) {
             // resume rat ratchet on following rat change within the same location
-            mVoiceRatchetEnabled = true;
+            mDataRatchetEnabled = true;
         }
 
         boolean newUsingCA = oldSS.isUsingCarrierAggregation()
@@ -136,6 +136,10 @@
 
     private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
         synchronized (mRatFamilyMap) {
+            // Either the two technologies are the same or their families must be non-null
+            // and the same.
+            if (ss1.getRilDataRadioTechnology() == ss2.getRilDataRadioTechnology()) return true;
+            if (mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) == null) return false;
             return mRatFamilyMap.get(ss1.getRilDataRadioTechnology())
                     == mRatFamilyMap.get(ss2.getRilDataRadioTechnology());
         }
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index e26a0f5..1e5afc7 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -1454,7 +1454,13 @@
         // fields need to be public for derived SmsDispatchers
         private final HashMap<String, Object> mData;
         public int mRetryCount;
-        public int mImsRetry; // nonzero indicates initial message was sent over Ims
+        // IMS retry counter. Nonzero indicates initial message was sent over IMS channel in RIL and
+        // counts how many retries have been made on the IMS channel.
+        // Used in older implementations where the message is sent over IMS using the RIL.
+        public int mImsRetry;
+        // Tag indicating that this SMS is being handled by the ImsSmsDispatcher. This tracker
+        // should not try to use SMS over IMS over the RIL interface in this case when falling back.
+        public boolean mUsesImsServiceForIms;
         public int mMessageRef;
         public boolean mExpectMore;
         public int mValidityPeriod;
@@ -1504,6 +1510,7 @@
             mFormat = format;
             mExpectMore = expectMore;
             mImsRetry = 0;
+            mUsesImsServiceForIms = false;
             mMessageRef = 0;
             mUnsentPartCount = unsentPartCount;
             mAnyPartFailed = anyPartFailed;
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 4a6321a..db566ef 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -131,6 +131,7 @@
     private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
     private long mLastCellInfoListTime;
     private List<CellInfo> mLastCellInfoList = null;
+    private List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = null;
 
     private SignalStrength mSignalStrength;
 
@@ -582,6 +583,11 @@
         }
 
         // If we are previously in service, we need to notify that we are out of service now.
+        if (mSS != null && mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+            mNetworkDetachedRegistrants.notifyRegistrants();
+        }
+
+        // If we are previously in service, we need to notify that we are out of service now.
         if (mSS != null && mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
             mDetachedRegistrants.notifyRegistrants();
         }
@@ -671,6 +677,10 @@
         mCi.unregisterForImsNetworkStateChanged(this);
         mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
                 CARRIER_ACTION_SET_RADIO_ENABLED);
+        if (mCSST != null) {
+            mCSST.dispose();
+            mCSST = null;
+        }
     }
 
     public boolean getDesiredPowerState() {
@@ -1450,6 +1460,7 @@
                                 + list);
                     }
                     mPhone.notifyPhysicalChannelConfiguration(list);
+                    mLastPhysicalChannelConfigList = list;
 
                     // only notify if bandwidths changed
                     if (RatRatcheter.updateBandwidths(getBandwidthsFromConfigs(list), mSS)) {
@@ -1789,7 +1800,7 @@
                 mNewSS.setCssIndicator(cssIndicator);
                 mNewSS.setRilVoiceRadioTechnology(newVoiceRat);
                 mNewSS.addNetworkRegistrationState(networkRegState);
-                setChannelNumberFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+                setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
 
                 //Denial reason if registrationState = 3
                 int reasonForDenial = networkRegState.getReasonForDenial();
@@ -1865,7 +1876,14 @@
                 mNewSS.setDataRegState(serviceState);
                 mNewSS.setRilDataRadioTechnology(newDataRat);
                 mNewSS.addNetworkRegistrationState(networkRegState);
-                setChannelNumberFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+
+                // When we receive OOS reset the PhyChanConfig list so that non-return-to-idle
+                // implementers of PhyChanConfig unsol will not carry forward a CA report
+                // (2 or more cells) to a new cell if they camp for emergency service only.
+                if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+                    mLastPhysicalChannelConfigList = null;
+                }
+                setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
 
                 if (mPhone.isPhoneTypeGsm()) {
 
@@ -2004,7 +2022,22 @@
         }
     }
 
-    private void setChannelNumberFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
+    private static boolean isValidLteBandwidthKhz(int bandwidth) {
+        // Valid bandwidths, see 3gpp 36.101 sec. 5.6
+        switch (bandwidth) {
+            case 1400:
+            case 3000:
+            case 5000:
+            case 10000:
+            case 15000:
+            case 20000:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void setPhyCellInfoFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
         if (cellIdentity == null) {
             if (DBG) {
                 log("Could not set ServiceState channel number. CellIdentity null");
@@ -2016,6 +2049,49 @@
         if (VDBG) {
             log("Setting channel number: " + cellIdentity.getChannelNumber());
         }
+
+        if (cellIdentity instanceof CellIdentityLte) {
+            CellIdentityLte cl = (CellIdentityLte) cellIdentity;
+            int[] bandwidths = null;
+            // Prioritize the PhysicalChannelConfig list because we might already be in carrier
+            // aggregation by the time poll state is performed.
+            if (!ArrayUtils.isEmpty(mLastPhysicalChannelConfigList)) {
+                bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
+                for (int bw : bandwidths) {
+                    if (!isValidLteBandwidthKhz(bw)) {
+                        loge("Invalid LTE Bandwidth in RegistrationState, " + bw);
+                        bandwidths = null;
+                        break;
+                    }
+                }
+            }
+            // If we don't have a PhysicalChannelConfig[] list, then pull from CellIdentityLte.
+            // This is normal if we're in idle mode and the PhysicalChannelConfig[] has already
+            // been updated. This is also a fallback in case the PhysicalChannelConfig info
+            // is invalid (ie, broken).
+            // Also, for vendor implementations that do not report return-to-idle, we should
+            // prioritize the bandwidth report in the CellIdentity, because the physical channel
+            // config report may be stale in the case where a single carrier was used previously
+            // and we transition to camped-for-emergency (since we never have a physical
+            // channel active). In the normal case of single-carrier non-return-to-idle, the
+            // values *must* be the same, so it doesn't matter which is chosen.
+            if (bandwidths == null || bandwidths.length == 1) {
+                final int cbw = cl.getBandwidth();
+                if (isValidLteBandwidthKhz(cbw)) {
+                    bandwidths = new int[] {cbw};
+                } else if (cbw == Integer.MAX_VALUE) {
+                    // Bandwidth is unreported; c'est la vie. This is not an error because
+                    // pre-1.2 HAL implementations do not support bandwidth reporting.
+                } else {
+                    loge("Invalid LTE Bandwidth in RegistrationState, " + cbw);
+                }
+            }
+            if (bandwidths != null) {
+                ss.setCellBandwidths(bandwidths);
+            }
+        } else {
+            if (VDBG) log("Skipping bandwidth update for Non-LTE cell.");
+        }
     }
 
     /**
@@ -2684,7 +2760,7 @@
         // until cell change or device is OOS
         boolean isDataInService = mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
 
-        if (!hasLocationChanged && isDataInService) {
+        if (isDataInService) {
             mRatRatcheter.ratchet(mSS, mNewSS, hasLocationChanged);
         }
 
@@ -2868,8 +2944,9 @@
             if (isInvalidOperatorNumeric(operatorNumeric)) {
                 if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid");
                 // Passing empty string is important for the first update. The initial value of
-                // operator numeric in locale tracker is null.
-                mLocaleTracker.updateOperatorNumeric("");
+                // 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.updateOperatorNumericAsync("");
                 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
@@ -2881,7 +2958,7 @@
                     setOperatorIdd(operatorNumeric);
                 }
 
-                mLocaleTracker.updateOperatorNumeric(operatorNumeric);
+                mLocaleTracker.updateOperatorNumericSync(operatorNumeric);
                 String countryIsoCode = mLocaleTracker.getCurrentCountry();
 
                 // Update Time Zone.
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 500094b..fd12273 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -162,6 +162,7 @@
     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
 
     private int[] colorArr;
+    private long mLastISubServiceRegTime;
 
     public static SubscriptionController init(Phone phone) {
         synchronized (SubscriptionController.class) {
@@ -207,7 +208,8 @@
         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
 
         if(ServiceManager.getService("isub") == null) {
-                ServiceManager.addService("isub", this);
+            ServiceManager.addService("isub", this);
+            mLastISubServiceRegTime = System.currentTimeMillis();
         }
 
         if (DBG) logdl("[SubscriptionController] init by Context");
@@ -2135,6 +2137,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             pw.println("SubscriptionController:");
+            pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime);
             pw.println(" defaultSubId=" + getDefaultSubId());
             pw.println(" defaultDataSubId=" + getDefaultDataSubId());
             pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index e0f1fd4..afd49fa 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -600,6 +600,8 @@
                             + Integer.toString(mInsertSimState[i]), i);
                     logd("SUB" + (i + 1) + " has invalid IccId");
                 } else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
+                    logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
+                            + mIccId[i] + "slot: " + i);
                     mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
                 }
                 if (isNewSim(mIccId[i], decIccId[i], oldIccId)) {
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 462a1b1..8ad5720 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -104,8 +104,8 @@
      * Create a new UiccProfile object.
      */
     public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics,
-                                       int phoneId, UiccCard uiccCard) {
-        return new UiccProfile(context, ci, ics, phoneId, uiccCard);
+                                       int phoneId, UiccCard uiccCard, Object lock) {
+        return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock);
     }
 
     public EriManager makeEriManager(Phone phone, Context context, int eriFileSource) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index b145b4b..bbaa3ca 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -16,12 +16,7 @@
 
 package com.android.internal.telephony.cdma;
 
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
 import android.os.Message;
-import android.provider.Telephony.Sms;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
@@ -124,6 +119,7 @@
                 + " mRetryCount=" + tracker.mRetryCount
                 + " mImsRetry=" + tracker.mImsRetry
                 + " mMessageRef=" + tracker.mMessageRef
+                + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + mPhone.getServiceState().getState());
 
         int ss = mPhone.getServiceState().getState();
@@ -147,8 +143,11 @@
         // sms over cdma is used:
         //   if sms over IMS is not supported AND
         //   this is not a retry case after sms over IMS failed
-        //     indicated by mImsRetry > 0
-        if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {
+        //     indicated by mImsRetry > 0 OR
+        //   SMS over IMS is disabled because of the network type OR
+        //   SMS over IMS is being handled by the ImsSmsDispatcher implementation and has indicated
+        //   that the message should fall back to sending over CS.
+        if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled || tracker.mUsesImsServiceForIms) {
             mCi.sendCdmaSms(pdu, reply);
         } else {
             mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index 9394ecb..6f1c8a6 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.hardware.radio.V1_0.ApnTypes;
 import android.os.PersistableBundle;
+import android.provider.Telephony.Carriers;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -50,6 +51,7 @@
     static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
     static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
     static final String V4_FORMAT_REGEX = "^\\[ApnSettingV4\\]\\s*";
+    static final String V5_FORMAT_REGEX = "^\\[ApnSettingV5\\]\\s*";
     static final String TAG = "ApnSetting";
 
     public final String carrier;
@@ -129,6 +131,16 @@
     public final String mvnoMatchData;
 
     /**
+     * The APN set id.
+     *
+     * APNs that are part of the same set should be preferred together, e.g. if the
+     * user selects a default APN with apnSetId=1, then we will prefer all APNs with apnSetId=1.
+     *
+     * If the apnSetId=Carriers.NO_SET_SET (=0) then the APN is not part of a set.
+     */
+    public final int apnSetId;
+
+    /**
      * Indicates this APN setting is permanently failed and cannot be
      * retried by the retry manager anymore.
      * */
@@ -179,10 +191,26 @@
         this.mtu = mtu;
         this.mvnoType = mvnoType;
         this.mvnoMatchData = mvnoMatchData;
+        this.apnSetId = Carriers.NO_SET_SET;
         this.networkTypeBitmask = ServiceState.convertBearerBitmaskToNetworkTypeBitmask(
                 this.bearerBitmask);
     }
 
+    // Constructor with default apn set id
+    public ApnSetting(int id, String numeric, String carrier, String apn,
+                      String proxy, String port,
+                      String mmsc, String mmsProxy, String mmsPort,
+                      String user, String password, int authType, String[] types,
+                      String protocol, String roamingProtocol, boolean carrierEnabled,
+                      int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
+                      int waitTime, int maxConnsTime, int mtu, String mvnoType,
+                      String mvnoMatchData) {
+        this(id, numeric, carrier, apn, proxy, port, mmsc, mmsProxy, mmsPort, user, password,
+                authType, types, protocol, roamingProtocol, carrierEnabled, networkTypeBitmask,
+                profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType,
+                mvnoMatchData, Carriers.NO_SET_SET);
+    }
+
     public ApnSetting(int id, String numeric, String carrier, String apn,
                       String proxy, String port,
                       String mmsc, String mmsProxy, String mmsPort,
@@ -190,7 +218,7 @@
                       String protocol, String roamingProtocol, boolean carrierEnabled,
                       int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
                       int waitTime, int maxConnsTime, int mtu, String mvnoType,
-                      String mvnoMatchData) {
+                      String mvnoMatchData, int apnSetId) {
         this.id = id;
         this.numeric = numeric;
         this.carrier = carrier;
@@ -225,6 +253,7 @@
         this.mtu = mtu;
         this.mvnoType = mvnoType;
         this.mvnoMatchData = mvnoMatchData;
+        this.apnSetId = apnSetId;
     }
 
     public ApnSetting(ApnSetting apn) {
@@ -232,7 +261,7 @@
                 apn.mmsPort, apn.user, apn.password, apn.authType, apn.types, apn.protocol,
                 apn.roamingProtocol, apn.carrierEnabled, apn.networkTypeBitmask, apn.profileId,
                 apn.modemCognitive, apn.maxConns, apn.waitTime, apn.maxConnsTime,
-                apn.mtu, apn.mvnoType, apn.mvnoMatchData);
+                apn.mtu, apn.mvnoType, apn.mvnoMatchData, apn.apnSetId);
     }
 
     /**
@@ -267,6 +296,13 @@
      *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
      *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>
      *
+     * v5 format:
+     *   [ApnSettingV5] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
+     *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
+     *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
+     *   <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId>
+     *
      * Note that the strings generated by toString() do not contain the username
      * and password and thus cannot be read by this method.
      */
@@ -275,7 +311,10 @@
 
         int version;
         // matches() operates on the whole string, so append .* to the regex.
-        if (data.matches(V4_FORMAT_REGEX + ".*")) {
+        if (data.matches(V5_FORMAT_REGEX + ".*")) {
+            version = 5;
+            data = data.replaceFirst(V5_FORMAT_REGEX, "");
+        } else if (data.matches(V4_FORMAT_REGEX + ".*")) {
             version = 4;
             data = data.replaceFirst(V4_FORMAT_REGEX, "");
         } else if (data.matches(V3_FORMAT_REGEX + ".*")) {
@@ -313,6 +352,7 @@
         int mtu = PhoneConstants.UNSET_MTU;
         String mvnoType = "";
         String mvnoMatchData = "";
+        int apnSetId = Carriers.NO_SET_SET;
         if (version == 1) {
             typeArray = new String[a.length - 13];
             System.arraycopy(a, 13, typeArray, 0, a.length - 13);
@@ -353,6 +393,9 @@
             if (a.length > 26) {
                 networkTypeBitmask = ServiceState.getBitmaskFromString(a[26]);
             }
+            if (a.length > 27) {
+                apnSetId = Integer.parseInt(a[27]);
+            }
         }
 
         // If both bearerBitmask and networkTypeBitmask were specified, bearerBitmask would be
@@ -364,7 +407,7 @@
         return new ApnSetting(-1, a[10] + a[11], a[0], a[1], a[2], a[3], a[7], a[8], a[9], a[4],
                 a[5], authType, typeArray, protocol, roamingProtocol, carrierEnabled,
                 networkTypeBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime,
-                mtu, mvnoType, mvnoMatchData);
+                mtu, mvnoType, mvnoMatchData, apnSetId);
     }
 
     /**
@@ -393,7 +436,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV4] ")
+        sb.append("[ApnSettingV5] ")
         .append(carrier)
         .append(", ").append(id)
         .append(", ").append(numeric)
@@ -425,6 +468,7 @@
         sb.append(", ").append(mvnoMatchData);
         sb.append(", ").append(permanentFailed);
         sb.append(", ").append(networkTypeBitmask);
+        sb.append(", ").append(apnSetId);
         return sb.toString();
     }
 
@@ -651,7 +695,8 @@
                 && mtu == other.mtu
                 && mvnoType.equals(other.mvnoType)
                 && mvnoMatchData.equals(other.mvnoMatchData)
-                && networkTypeBitmask == other.networkTypeBitmask;
+                && networkTypeBitmask == other.networkTypeBitmask
+                && apnSetId == other.apnSetId;
     }
 
     /**
@@ -696,7 +741,8 @@
                 && maxConnsTime == other.maxConnsTime
                 && mtu == other.mtu
                 && mvnoType.equals(other.mvnoType)
-                && mvnoMatchData.equals(other.mvnoMatchData);
+                && mvnoMatchData.equals(other.mvnoMatchData)
+                && apnSetId == other.apnSetId;
     }
 
     /**
@@ -722,7 +768,8 @@
                 && xorEquals(this.mmsc, other.mmsc)
                 && xorEquals(this.mmsProxy, other.mmsProxy)
                 && xorEquals(this.mmsPort, other.mmsPort))
-                && this.networkTypeBitmask == other.networkTypeBitmask;
+                && this.networkTypeBitmask == other.networkTypeBitmask
+                && this.apnSetId == other.apnSetId;
     }
 
     // check whether the types of two APN same (even only one type of each APN is same)
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index a400f21..0696aa9 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -936,7 +936,6 @@
         result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         if (mApnSetting != null) {
-            ApnSetting securedDunApn = mDct.fetchDunApn();
             for (String type : mApnSetting.types) {
                 if (!mRestrictedNetworkOverride
                         && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
@@ -953,11 +952,7 @@
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
-                        // check if this is the DUN apn as well as returned by fetchDunApn().
-                        // If yes, add DUN capability too.
-                        if (mApnSetting.equals(securedDunApn)) {
-                            result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-                        }
+                        result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -973,9 +968,7 @@
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DUN: {
-                        if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
-                            result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-                        }
+                        result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
                         break;
                     }
                     case PhoneConstants.APN_TYPE_FOTA: {
@@ -1725,6 +1718,7 @@
             misc.subscriberId = mPhone.getSubscriberId();
 
             setNetworkRestriction();
+            if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride);
             mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
                     "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
                     50, misc);
@@ -2169,7 +2163,6 @@
                 String logStr = "Changed from " + mNetworkCapabilities + " to "
                         + networkCapabilities + ", Data RAT="
                         + mPhone.getServiceState().getRilDataRadioTechnology()
-                        + ", DUN APN=\"" + mDct.fetchDunApn() + "\""
                         + ", mApnSetting=" + mApnSetting;
                 mNetCapsLocalLog.log(logStr);
                 log(logStr);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index bdd334f..38d3852 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static android.Manifest.permission.READ_PHONE_STATE;
+
+import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.ProgressDialog;
@@ -1706,27 +1709,27 @@
     }
 
     /**
-     * Fetch dun apn
-     * @return ApnSetting to be used for dun
+     * Fetch the DUN apns
+     * @return a list of DUN ApnSetting objects
      */
     @VisibleForTesting
-    public ApnSetting fetchDunApn() {
+    public @NonNull ArrayList<ApnSetting> fetchDunApns() {
         if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
-            log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
-            return null;
+            log("fetchDunApns: net.tethering.noprovisioning=true ret: empty list");
+            return new ArrayList<ApnSetting>(0);
         }
         int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
         IccRecords r = mIccRecords.get();
         String operator = (r != null) ? r.getOperatorNumeric() : "";
         ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
-        ApnSetting retDunSetting = null;
+        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).
         String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
         if (!TextUtils.isEmpty(apnData)) {
             dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
-            if (VDBG) log("fetchDunApn: dunCandidates from Setting: " + dunCandidates);
+            if (VDBG) log("fetchDunApns: dunCandidates from Setting: " + dunCandidates);
         }
 
         // todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
@@ -1741,7 +1744,7 @@
                     // apn may be null if apnString isn't valid or has error parsing
                     if (apn != null) dunCandidates.add(apn);
                 }
-                if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
+                if (VDBG) log("fetchDunApns: dunCandidates from resource: " + dunCandidates);
             }
         }
 
@@ -1752,7 +1755,7 @@
                         dunCandidates.add(apn);
                     }
                 }
-                if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+                if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates);
             }
         }
 
@@ -1765,24 +1768,36 @@
                 if (dunSetting.hasMvnoParams()) {
                     if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
                             dunSetting.mvnoMatchData)) {
-                        retDunSetting = dunSetting;
-                        break;
+                        retDunSettings.add(dunSetting);
                     }
                 } else if (mMvnoMatched == false) {
-                    retDunSetting = dunSetting;
-                    break;
+                    retDunSettings.add(dunSetting);
                 }
             }
         }
 
-        if (VDBG) log("fetchDunApn: dunSetting=" + retDunSetting);
-        return retDunSetting;
+        if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
+        return retDunSettings;
+    }
+
+    private int getPreferredApnSetId() {
+        Cursor c = mPhone.getContext().getContentResolver()
+                .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
+                    "preferapnset/subId/" + mPhone.getSubId()),
+                        new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null);
+        if (c.getCount() < 1) {
+            loge("getPreferredApnSetId: no APNs found");
+            return Telephony.Carriers.NO_SET_SET;
+        } else {
+            c.moveToFirst();
+            return c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
+        }
     }
 
     public boolean hasMatchedTetherApnSetting() {
-        ApnSetting matched = fetchDunApn();
-        log("hasMatchedTetherApnSetting: APN=" + matched);
-        return matched != null;
+        ArrayList<ApnSetting> matches = fetchDunApns();
+        log("hasMatchedTetherApnSetting: APNs=" + matches);
+        return matches.size() > 0;
     }
 
     /**
@@ -1793,7 +1808,8 @@
         final int rilRat = mPhone.getServiceState().getRilDataRadioTechnology();
         if (ServiceState.isCdma(rilRat)) return true;
 
-        return (fetchDunApn() != null);
+        ArrayList<ApnSetting> apns = fetchDunApns();
+        return apns.size() > 0;
     }
 
     /**
@@ -1876,7 +1892,8 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)));
         return apn;
     }
 
@@ -2465,10 +2482,10 @@
 
     private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
         String apnType = apnContext.getApnType();
-        ApnSetting dunSetting = null;
+        ArrayList<ApnSetting> dunSettings = null;
 
         if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
-            dunSetting = fetchDunApn();
+            dunSettings = sortApnListByPreferred(fetchDunApns());
         }
         if (DBG) {
             log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
@@ -2481,23 +2498,26 @@
             if (curDcac != null) {
                 ApnSetting apnSetting = curApnCtx.getApnSetting();
                 log("apnSetting: " + apnSetting);
-                if (dunSetting != null) {
-                    if (dunSetting.equals(apnSetting)) {
-                        switch (curApnCtx.getState()) {
-                            case CONNECTED:
-                                if (DBG) {
-                                    log("checkForCompatibleConnectedApnContext:"
-                                            + " found dun conn=" + curDcac
-                                            + " curApnCtx=" + curApnCtx);
-                                }
-                                return curDcac;
-                            case RETRYING:
-                            case CONNECTING:
-                                potentialDcac = curDcac;
-                                potentialApnCtx = curApnCtx;
-                            default:
-                                // Not connected, potential unchanged
-                                break;
+                if (dunSettings != null && dunSettings.size() > 0) {
+                    for (ApnSetting dunSetting : dunSettings) {
+                        if (dunSetting.equals(apnSetting)) {
+                            switch (curApnCtx.getState()) {
+                                case CONNECTED:
+                                    if (DBG) {
+                                        log("checkForCompatibleConnectedApnContext:"
+                                                + " found dun conn=" + curDcac
+                                                + " curApnCtx=" + curApnCtx);
+                                    }
+                                    return curDcac;
+                                case RETRYING:
+                                case CONNECTING:
+                                    potentialDcac = curDcac;
+                                    potentialApnCtx = curApnCtx;
+                                    break;
+                                default:
+                                    // Not connected, potential unchanged
+                                    break;
+                            }
                         }
                     }
                 } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
@@ -2513,6 +2533,7 @@
                         case CONNECTING:
                             potentialDcac = curDcac;
                             potentialApnCtx = curApnCtx;
+                            break;
                         default:
                             // Not connected, potential unchanged
                             break;
@@ -3406,7 +3427,7 @@
                 dest.authType, resultTypes.toArray(new String[0]), protocol,
                 roamingProtocol, dest.carrierEnabled, networkTypeBitmask, dest.profileId,
                 (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
-                dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
+                dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData, dest.apnSetId);
     }
 
     /** Return the DC AsyncChannel for the new data connection */
@@ -3450,11 +3471,13 @@
         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
 
         if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
-            ApnSetting dun = fetchDunApn();
-            if (dun != null) {
-                apnList.add(dun);
-                if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
-                return apnList;
+            ArrayList<ApnSetting> dunApns = fetchDunApns();
+            if (dunApns.size() > 0) {
+                for (ApnSetting dun : dunApns) {
+                    apnList.add(dun);
+                    if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
+                }
+                return sortApnListByPreferred(apnList);
             }
         }
 
@@ -3495,6 +3518,7 @@
                 if (ServiceState.bitmaskHasTech(mPreferredApn.networkTypeBitmask,
                         ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
                     apnList.add(mPreferredApn);
+                    apnList = sortApnListByPreferred(apnList);
                     if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
                     return apnList;
                 } else {
@@ -3531,10 +3555,41 @@
         } else {
             loge("mAllApnSettings is null!");
         }
+
+        apnList = sortApnListByPreferred(apnList);
         if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
         return apnList;
     }
 
+    /**
+     * Sort a list of ApnSetting objects, with the preferred APNs at the front of the list
+     *
+     * e.g. if the preferred APN set = 2 and we have
+     *   1. APN with apn_set_id = 0 = Carriers.NO_SET_SET (no set is set)
+     *   2. APN with apn_set_id = 1 (not preferred set)
+     *   3. APN with apn_set_id = 2 (preferred set)
+     * Then the return order should be (3, 1, 2) or (3, 2, 1)
+     *
+     * e.g. if the preferred APN set = Carriers.NO_SET_SET (no preferred set) then the
+     * return order can be anything
+     */
+    @VisibleForTesting
+    public ArrayList<ApnSetting> sortApnListByPreferred(ArrayList<ApnSetting> list) {
+        if (list == null || list.size() <= 1) return list;
+        int preferredApnSetId = getPreferredApnSetId();
+        if (preferredApnSetId != Telephony.Carriers.NO_SET_SET) {
+            list.sort(new Comparator<ApnSetting>() {
+                @Override
+                public int compare(ApnSetting apn1, ApnSetting apn2) {
+                    if (apn1.apnSetId == preferredApnSetId) return -1;
+                    if (apn2.apnSetId == preferredApnSetId) return 1;
+                    return 0;
+                }
+            });
+        }
+        return list;
+    }
+
     private String apnListToString (ArrayList<ApnSetting> apns) {
         StringBuilder result = new StringBuilder();
         for (int i = 0, size = apns.size(); i < size; i++) {
@@ -4571,11 +4626,20 @@
         if (VDBG_STALL) log("putRecoveryAction: " + action);
     }
 
+    private void broadcastDataStallDetected(int recoveryAction) {
+        Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
+        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
+        intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
+        mPhone.getContext().sendBroadcast(intent, READ_PHONE_STATE);
+    }
+
     private void doRecovery() {
         if (getOverallState() == DctConstants.State.CONNECTED) {
             // Go through a series of recovery steps, each action transitions to the next action
             final int recoveryAction = getRecoveryAction();
             TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
+            broadcastDataStallDetected(recoveryAction);
+
             switch (recoveryAction) {
                 case RecoveryAction.GET_DATA_CALL_LIST:
                     EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index d3c59a3..5c5d244 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -220,6 +220,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getAllProfiles callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getAllProfiles callback failure.", exception);
@@ -258,6 +259,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("getProfile callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("getProfile callback failure.", exception);
@@ -296,6 +298,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("disableProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("disableProfile callback failure.", exception);
@@ -338,6 +341,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("switchToProfile callback onException: ", e);
                             callback.onComplete(getResultCode(e), profile);
                         } catch (RemoteException exception) {
                             loge("switchToProfile callback failure.", exception);
@@ -351,6 +355,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getProfile in switchToProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("switchToProfile callback failure.", exception);
@@ -389,6 +394,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("setNickname callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("setNickname callback failure.", exception);
@@ -417,19 +423,19 @@
         AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
             @Override
             public void onResult(Void result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK);
-                            } catch (RemoteException exception) {
-                                loge("deleteProfile callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after delete.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK);
+                } catch (RemoteException exception) {
+                    loge("deleteProfile callback failure.", exception);
+                }
             };
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("deleteProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("deleteProfile callback failure.", exception);
@@ -458,19 +464,19 @@
         AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
             @Override
             public void onResult(Void result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK);
-                            } catch (RemoteException exception) {
-                                loge("resetMemory callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after reset memory.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK);
+                } catch (RemoteException exception) {
+                    loge("resetMemory callback failure.", exception);
+                }
             }
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("resetMemory callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("resetMemory callback failure.", exception);
@@ -509,6 +515,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getDefaultSmdpAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getDefaultSmdpAddress callback failure.", exception);
@@ -547,6 +554,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getSmdsAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getSmdsAddress callback failure.", exception);
@@ -585,6 +593,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("setDefaultSmdpAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("setDefaultSmdpAddress callback failure.", exception);
@@ -624,6 +633,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getRulesAuthTable callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getRulesAuthTable callback failure.", exception);
@@ -662,6 +672,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccChallenge callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccChallenge callback failure.", exception);
@@ -700,6 +711,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccInfo1 callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccInfo1 callback failure.", exception);
@@ -738,6 +750,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccInfo2 callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccInfo2 callback failure.", exception);
@@ -777,6 +790,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("authenticateServer callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("authenticateServer callback failure.", exception);
@@ -817,6 +831,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("prepareDownload callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("prepareDownload callback failure.", exception);
@@ -846,19 +861,19 @@
         AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
             @Override
             public void onResult(byte[] result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK, result);
-                            } catch (RemoteException exception) {
-                                loge("loadBoundProfilePackage callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after install.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK, result);
+                } catch (RemoteException exception) {
+                    loge("loadBoundProfilePackage callback failure.", exception);
+                }
             }
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("loadBoundProfilePackage callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("loadBoundProfilePackage callback failure.", exception);
@@ -897,6 +912,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("cancelSession callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("cancelSession callback failure.", exception);
@@ -936,6 +952,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("listNotifications callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("listNotifications callback failure.", exception);
@@ -975,6 +992,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("retrieveNotificationList callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("retrieveNotificationList callback failure.", exception);
@@ -1014,6 +1032,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("retrieveNotification callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("retrieveNotification callback failure.", exception);
@@ -1053,6 +1072,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("removeNotificationFromList callback onException: ", e);
                             callback.onComplete(getResultCode(e));
                         } catch (RemoteException exception) {
                             loge("removeNotificationFromList callback failure.", exception);
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 8c1b81e..ca36cf0 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -24,6 +24,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ComponentInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -984,6 +985,10 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void sendOtaStatusChangedBroadcast() {
         Intent intent = new Intent(EuiccManager.ACTION_OTA_STATUS_CHANGED);
+        ComponentInfo bestComponent = mConnector.findBestComponent(mContext.getPackageManager());
+        if (bestComponent != null) {
+            intent.setPackage(bestComponent.packageName);
+        }
         mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
     }
 
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index ef43430..99082ee 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -16,13 +16,8 @@
 
 package com.android.internal.telephony.gsm;
 
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.provider.Telephony.Sms;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -188,6 +183,7 @@
                 + " mRetryCount=" + tracker.mRetryCount
                 + " mImsRetry=" + tracker.mImsRetry
                 + " mMessageRef=" + tracker.mMessageRef
+                + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
                 + " SS=" + mPhone.getServiceState().getState());
 
         int ss = mPhone.getServiceState().getState();
@@ -203,8 +199,11 @@
         // sms over gsm is used:
         //   if sms over IMS is not supported AND
         //   this is not a retry case after sms over IMS failed
-        //     indicated by mImsRetry > 0
-        if (0 == tracker.mImsRetry && !isIms()) {
+        //     indicated by mImsRetry > 0 OR
+        //   this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
+        //     this message because the ImsSmsDispatcher has indicated that the message needs to
+        //     fall back to sending over CS.
+        if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
             if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
                 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
                         IccUtils.bytesToHexString(pdu), reply);
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
index 8eec9db..7b0619b 100644
--- a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -155,6 +155,10 @@
 
         @Override
         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+            // At de-registration, notify the framework that no IMS capabilities are currently
+            // available.
+            Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities.");
+            notifyCapabilitiesStatusChanged(new MmTelCapabilities());
             // Implemented in the Registration Adapter
         }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index d23aad7..4c858cc 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -2884,7 +2884,6 @@
                 @Override
                 public void onDeregistered(ImsReasonInfo imsReasonInfo) {
                     if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
-                    resetImsCapabilities();
                     mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
                     mPhone.setImsRegistered(false);
                     mPhone.processDisconnectReason(imsReasonInfo);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 04ae527..d63b9c2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -996,11 +996,29 @@
     }
 
     public void onRttMessageReceived(String message) {
-        getOrCreateRttTextHandler().sendToInCall(message);
+        synchronized (this) {
+            if (mRttTextHandler == null) {
+                Rlog.w(LOG_TAG, "onRttMessageReceived: RTT text handler not available."
+                        + " Attempting to create one.");
+                if (mRttTextStream == null) {
+                    Rlog.e(LOG_TAG, "onRttMessageReceived:"
+                            + " Unable to process incoming message. No textstream available");
+                    return;
+                }
+                createRttTextHandler();
+            }
+        }
+        mRttTextHandler.sendToInCall(message);
     }
 
     public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
-        mRttTextStream = rttTextStream;
+        synchronized (this) {
+            mRttTextStream = rttTextStream;
+            if (mRttTextHandler == null && mIsRttEnabledForCall) {
+                Rlog.i(LOG_TAG, "setCurrentRttTextStream: Creating a text handler");
+                createRttTextHandler();
+            }
+        }
     }
 
     public boolean hasRttTextStream() {
@@ -1012,20 +1030,24 @@
     }
 
     public void startRttTextProcessing() {
-        if (mRttTextStream == null) {
-            Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
-            return;
+        synchronized (this) {
+            if (mRttTextStream == null) {
+                Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
+                return;
+            }
+            if (mRttTextHandler != null) {
+                Rlog.w(LOG_TAG, "startRttTextProcessing: RTT text handler already exists");
+                return;
+            }
+            createRttTextHandler();
         }
-        getOrCreateRttTextHandler().initialize(mRttTextStream);
     }
 
-    private ImsRttTextHandler getOrCreateRttTextHandler() {
-        if (mRttTextHandler != null) {
-            return mRttTextHandler;
-        }
+    // Make sure to synchronize on ImsPhoneConnection.this before calling.
+    private void createRttTextHandler() {
         mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
                 (message) -> getImsCall().sendRttMessage(message));
-        return mRttTextHandler;
+        mRttTextHandler.initialize(mRttTextStream);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index a59e186..e55170a 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -46,7 +46,9 @@
     public static final String EXTRA_ICC_CARD_ADDED =
             "com.android.internal.telephony.uicc.ICC_CARD_ADDED";
 
-    private final Object mLock = new Object();
+    // 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;
     private CardState mCardState;
     private String mIccid;
     protected String mCardId;
@@ -55,10 +57,11 @@
     private CommandsInterface mCi;
     private final int mPhoneId;
 
-    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
         if (DBG) log("Creating");
         mCardState = ics.mCardState;
         mPhoneId = phoneId;
+        mLock = lock;
         update(c, ci, ics);
     }
 
@@ -83,7 +86,7 @@
             if (mCardState != CardState.CARDSTATE_ABSENT) {
                 if (mUiccProfile == null) {
                     mUiccProfile = TelephonyComponentFactory.getInstance().makeUiccProfile(
-                            mContext, mCi, ics, mPhoneId, this);
+                            mContext, mCi, ics, mPhoneId, this, mLock);
                 } else {
                     mUiccProfile.update(mContext, mCi, ics);
                 }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 201c6ff..e80ab1d 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -89,7 +89,9 @@
 
     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
 
-    private final Object mLock = new Object();
+    // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
+    // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
+    private final Object mLock;
     private PinState mUniversalPinState;
     private int mGsmUmtsSubscriptionAppIndex;
     private int mCdmaSubscriptionAppIndex;
@@ -98,7 +100,7 @@
             new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
     private Context mContext;
     private CommandsInterface mCi;
-    private UiccCard mUiccCard; //parent
+    private final UiccCard mUiccCard; //parent
     private CatService mCatService;
     private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
     private boolean mDisposed = false;
@@ -226,8 +228,9 @@
     };
 
     public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
-            UiccCard uiccCard) {
+            UiccCard uiccCard, Object lock) {
         if (DBG) log("Creating profile");
+        mLock = lock;
         mUiccCard = uiccCard;
         mPhoneId = phoneId;
         // set current app type based on phone type - do this before calling update() as that
@@ -254,19 +257,20 @@
      * Dispose the UiccProfile.
      */
     public void dispose() {
-        synchronized (mLock) {
-            if (DBG) log("Disposing profile");
+        if (DBG) log("Disposing profile");
 
+        // mUiccCard is outside of mLock in order to prevent deadlocking. This is safe because
+        // EuiccCard#unregisterForEidReady handles its own lock
+        if (mUiccCard instanceof EuiccCard) {
+            ((EuiccCard) mUiccCard).unregisterForEidReady(mHandler);
+        }
+        synchronized (mLock) {
             unregisterAllAppEvents();
             unregisterCurrAppEvents();
 
             InstallCarrierAppUtils.hideAllNotifications(mContext);
             InstallCarrierAppUtils.unregisterPackageInstallReceiver(mContext);
 
-            if (mUiccCard instanceof EuiccCard) {
-                ((EuiccCard) mUiccCard).unregisterForEidReady(mHandler);
-            }
-
             mCi.unregisterForOffOrNotAvailable(mHandler);
             mContext.unregisterReceiver(mReceiver);
 
@@ -859,15 +863,14 @@
 
     @Override
     public boolean hasIccCard() {
-        synchronized (mLock) {
-            if (mUiccCard != null && mUiccCard.getCardState()
-                    != IccCardStatus.CardState.CARDSTATE_ABSENT) {
-                return true;
-            }
-            loge("hasIccCard: UiccProfile is not null but UiccCard is null or card state is "
-                    + "ABSENT");
-            return false;
+        // mUiccCard is initialized in constructor, so won't be null
+        if (mUiccCard.getCardState()
+                != IccCardStatus.CardState.CARDSTATE_ABSENT) {
+            return true;
         }
+        loge("hasIccCard: UiccProfile is not null but UiccCard is null or card state is "
+                + "ABSENT");
+        return false;
     }
 
     /**
@@ -1524,6 +1527,15 @@
     }
 
     /**
+     * Reloads carrier privileges as if a change were just detected.  Useful to force a profile
+     * refresh without having to physically insert or remove a SIM card.
+     */
+    @VisibleForTesting
+    public void refresh() {
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+    }
+
+    /**
      * Dump
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index 5a6624b..25ef637 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -91,22 +91,8 @@
                 log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
             }
 
-            if (oldState != CardState.CARDSTATE_ABSENT
-                    && 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 (DBG) log("update: notify card removed");
-                    sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
-                }
-
-                UiccController.updateInternalIccState(
-                        IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId);
-
-                // no card present in the slot now; dispose card and make mUiccCard null
-                if (mUiccCard != null) {
-                    mUiccCard.dispose();
-                    nullifyUiccCard(false /* sim state is not unknown */);
-                }
+            if (absentStateUpdateNeeded(oldState)) {
+                updateCardStateAbsent();
             // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
             // create a new UiccCard instance in two scenarios:
             //   1. mCardState is changing from ABSENT to non ABSENT.
@@ -126,9 +112,9 @@
                 }
 
                 if (!mIsEuicc) {
-                    mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId);
+                    mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
                 } else {
-                    mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId);
+                    mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
                 }
             } else {
                 if (mUiccCard != null) {
@@ -145,8 +131,14 @@
     public void update(CommandsInterface ci, IccSlotStatus iss) {
         if (DBG) log("slotStatus update: " + iss.toString());
         synchronized (mLock) {
+            CardState oldState = mCardState;
             mCi = ci;
+            parseAtr(iss.atr);
+            mCardState = iss.cardState;
+            mIccId = iss.iccid;
             if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) {
+                // TODO: (b/79432584) evaluate whether should broadcast card state change
+                // even if it's inactive.
                 if (mActive) {
                     mActive = false;
                     mLastRadioState = RadioState.RADIO_UNAVAILABLE;
@@ -154,21 +146,44 @@
                     if (mUiccCard != null) mUiccCard.dispose();
                     nullifyUiccCard(true /* sim state is unknown */);
                 }
-                parseAtr(iss.atr);
-                mCardState = iss.cardState;
-                mIccId = iss.iccid;
-            } else if (!mActive && iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE) {
+            } else {
                 mActive = true;
-                parseAtr(iss.atr);
-                // todo - ignoring these fields for now; relying on sim state changed to update
-                // these
-                //      iss.cardState;
-                //      iss.iccid;
-                //      iss.logicalSlotIndex;
+                mPhoneId = iss.logicalSlotIndex;
+                if (absentStateUpdateNeeded(oldState)) {
+                    updateCardStateAbsent();
+                }
+                // TODO: (b/79432584) Create UiccCard or EuiccCard object here.
+                // Right now It's OK not creating it because Card status update will do it.
+                // But we should really make them symmetric.
             }
         }
     }
 
+    private boolean absentStateUpdateNeeded(CardState oldState) {
+        return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null)
+                && mCardState == CardState.CARDSTATE_ABSENT;
+    }
+
+    private void updateCardStateAbsent() {
+        RadioState radioState =
+                (mCi == null) ? RadioState.RADIO_UNAVAILABLE : mCi.getRadioState();
+        // No notifications while radio is off or we just powering up
+        if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+            if (DBG) log("update: notify card removed");
+            sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
+        }
+
+        UiccController.updateInternalIccState(
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId);
+
+        // no card present in the slot now; dispose card and make mUiccCard null
+        if (mUiccCard != null) {
+            mUiccCard.dispose();
+        }
+        nullifyUiccCard(false /* sim state is not unknown */);
+        mLastRadioState = radioState;
+    }
+
     // whenever we set mUiccCard to null, we lose the ability to differentiate between absent and
     // unknown states. To mitigate this, we will us mStateIsUnknown to keep track. The sim is only
     // unknown if we haven't heard from the radio or if the radio has become unavailable.
@@ -178,7 +193,7 @@
     }
 
     public boolean isStateUnknown() {
-        return mStateIsUnknown;
+        return (mCardState == null || mCardState == CardState.CARDSTATE_ABSENT) && mStateIsUnknown;
     }
 
     private void checkIsEuiccSupported() {
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 3ebfec2..b8b2818 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -116,8 +116,8 @@
     private EuiccSpecVersion mSpecVersion;
     private volatile String mEid;
 
-    public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
-        super(c, ci, ics, phoneId);
+    public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
+        super(c, ci, ics, phoneId, lock);
         // TODO: Set supportExtendedApdu based on ATR.
         mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
 
@@ -162,7 +162,12 @@
 
             @Override
             public void onException(Throwable e) {
-                // Not notifying registrants if getting eid fails.
+                // Still notifying registrants even getting eid fails.
+                if (mEidReadyRegistrants != null) {
+                    mEidReadyRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+                }
+                mEid = "";
+                mCardId = "";
                 Rlog.e(LOG_TAG, "Failed loading eid", e);
             }
         };
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index e81c9f4..529a798 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.isA;
@@ -27,18 +30,26 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Map;
+
 /**
  * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
  */
@@ -46,25 +57,34 @@
     public static final String LOG_TAG = "CSST";
     public static final int TEST_TIMEOUT = 5000;
 
+    private CarrierServiceStateTracker mSpyCarrierSST;
     private CarrierServiceStateTracker mCarrierSST;
     private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
-    private  CarrierServiceStateTracker.PrefNetworkNotification mPrefNetworkNotification;
-    private  CarrierServiceStateTracker.EmergencyNetworkNotification mEmergencyNetworkNotification;
+    private FakeContentResolver mFakeContentResolver;
 
-    @Mock Context mContext;
-    @Mock ServiceStateTracker mServiceStateTracker;
-    @Mock NotificationManager mNotificationManager;
-    @Mock Resources mResources;
+    NotificationManager mNotificationManager;
+    PersistableBundle mBundle;
+
+    private class FakeContentResolver extends MockContentResolver {
+        @Override
+        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+            super.notifyChange(uri, observer, syncToNetwork);
+            logd("onChanged(uri=" + uri + ")" + observer);
+            if (observer != null) {
+                observer.dispatchChange(false, uri);
+            }
+        }
+    }
 
     private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
-
         private CarrierServiceStateTrackerTestHandler(String name) {
             super(name);
         }
 
         @Override
         public void onLooperPrepared() {
-            mCarrierSST = spy(new CarrierServiceStateTracker(mPhone, mServiceStateTracker));
+            mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+            mSpyCarrierSST = spy(mCarrierSST);
             setReady(true);
         }
     }
@@ -74,15 +94,31 @@
         MockitoAnnotations.initMocks(this);
         logd(LOG_TAG + "Setup!");
         super.setUp(getClass().getSimpleName());
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        when(mPhone.getSubId()).thenReturn(1);
         mCarrierServiceStateTrackerTestHandler =
                 new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
         mCarrierServiceStateTrackerTestHandler.start();
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        mFakeContentResolver = new CarrierServiceStateTrackerTest.FakeContentResolver();
+
+        when(mPhone.getContext().getContentResolver()).thenReturn(mFakeContentResolver);
+
+        doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+
+        mNotificationManager = (NotificationManager) mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+
+        setDefaultValues();
         waitUntilReady();
     }
 
+    private void setDefaultValues() {
+        mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
+                0);
+        mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+                0);
+    }
+
     @After
     public void tearDown() throws Exception {
         mCarrierServiceStateTrackerTestHandler.quit();
@@ -93,12 +129,12 @@
     @SmallTest
     public void testCancelBothNotifications() {
         logd(LOG_TAG + ":testCancelBothNotifications()");
-        Message notificationMsg = mCarrierSST.obtainMessage(
+        Message notificationMsg = mSpyCarrierSST.obtainMessage(
                 CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
-        doReturn(false).when(mCarrierSST).evaluateSendingMessage(any());
-        doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
-        mCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+        doReturn(false).when(mSpyCarrierSST).evaluateSendingMessage(any());
+        doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+        mSpyCarrierSST.handleMessage(notificationMsg);
+        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
         verify(mNotificationManager).cancel(
                 CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
         verify(mNotificationManager).cancel(
@@ -110,18 +146,58 @@
     public void testSendBothNotifications() {
         logd(LOG_TAG + ":testSendBothNotifications()");
         Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
-        Message notificationMsg = mCarrierSST.obtainMessage(
+        Message notificationMsg = mSpyCarrierSST.obtainMessage(
                 CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
-        doReturn(true).when(mCarrierSST).evaluateSendingMessage(any());
-        doReturn(false).when(mCarrierSST).isRadioOffOrAirplaneMode();
-        doReturn(0).when(mCarrierSST).getDelay(any());
-        doReturn(mNotificationBuilder).when(mCarrierSST).getNotificationBuilder(any());
-        doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
-        mCarrierSST.handleMessage(notificationMsg);
-        waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+        doReturn(true).when(mSpyCarrierSST).evaluateSendingMessage(any());
+        doReturn(false).when(mSpyCarrierSST).isRadioOffOrAirplaneMode();
+        doReturn(0).when(mSpyCarrierSST).getDelay(any());
+        doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
+        doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+        mSpyCarrierSST.handleMessage(notificationMsg);
+        waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
         verify(mNotificationManager).notify(
                 eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK), any());
     }
+
+    @Test
+    @SmallTest
+    public void testSendPrefNetworkNotification() {
+        logd(LOG_TAG + ":testSendPrefNetworkNotification()");
+        Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        mContext.sendBroadcast(intent);
+        waitForMs(300);
+
+        Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+                mCarrierSST.getNotificationTypeMap();
+        CarrierServiceStateTracker.NotificationType prefNetworkNotification =
+                notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+        CarrierServiceStateTracker.NotificationType spyPrefNetworkNotification = spy(
+                prefNetworkNotification);
+        notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK,
+                spyPrefNetworkNotification);
+        Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getDataRegState();
+        doReturn(true).when(mSST).isRadioOn();
+        doReturn(mNotificationBuilder).when(spyPrefNetworkNotification).getNotificationBuilder();
+
+        String prefNetworkMode = Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId();
+        Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+        waitForMs(500);
+        verify(mNotificationManager).notify(
+                eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+
+        Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+        waitForMs(500);
+        verify(mNotificationManager, atLeast(1)).cancel(
+                CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 1fc47e3..fa8bec3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -61,6 +62,7 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
 import com.android.internal.telephony.uicc.IccException;
 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.telephony.uicc.UiccSlot;
 
@@ -70,6 +72,7 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.List;
 
@@ -179,6 +182,57 @@
 
     @Test
     @SmallTest
+    public void testGetSubscriberIdForGsmPhone() {
+        final String subscriberId = "123456789";
+        IccRecords iccRecords = Mockito.mock(IccRecords.class);
+        doReturn(subscriberId).when(iccRecords).getIMSI();
+        doReturn(iccRecords).when(mUiccController)
+                .getIccRecords(anyInt() /* phoneId */, eq(UiccController.APP_FAM_3GPP));
+
+        // Ensure the phone type is GSM
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(false).when(spyPhone).isPhoneTypeCdma();
+        doReturn(false).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(true).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetSubscriberIdForCdmaLtePhone() {
+        final String subscriberId = "abcdefghijk";
+        IccRecords iccRecords = Mockito.mock(IccRecords.class);
+        doReturn(subscriberId).when(iccRecords).getIMSI();
+        doReturn(iccRecords).when(mUiccController)
+                .getIccRecords(anyInt() /* phoneId */, eq(UiccController.APP_FAM_3GPP));
+
+        // Ensure the phone type is CdmaLte
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(false).when(spyPhone).isPhoneTypeCdma();
+        doReturn(true).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(false).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetSubscriberIdForCdmaPhone() {
+        final String subscriberId = "987654321";
+        doReturn(subscriberId).when(mSST).getImsi();
+
+        // Ensure the phone type is GSM
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(true).when(spyPhone).isPhoneTypeCdma();
+        doReturn(false).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(false).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
     public void testGetCellLocation() {
         // GSM
         CellLocation cellLocation = new GsmCellLocation();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
new file mode 100644
index 0000000..02f78ab
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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 static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellInfoGsm;
+import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class LocaleTrackerTest extends TelephonyTest {
+
+    private static final String US_MCC = "310";
+    private static final String FAKE_MNC = "123";
+    private static final String US_COUNTRY_CODE = "us";
+    private static final String COUNTRY_CODE_UNAVAILABLE = "";
+
+    private LocaleTracker mLocaleTracker;
+    private LocaleTrackerTestHandler mLocaleTrackerTestHandler;
+
+    private CellInfoGsm mCellInfo;
+    private WifiManager mWifiManager;
+
+    private class LocaleTrackerTestHandler extends HandlerThread {
+
+        private LocaleTrackerTestHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mLocaleTracker = new LocaleTracker(mPhone, this.getLooper());
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("LocaleTrackerTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+
+        // This is a workaround to bypass setting system properties, which causes access violation.
+        doReturn(-1).when(mPhone).getPhoneId();
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+
+        mCellInfo = new CellInfoGsm();
+        mCellInfo.setCellIdentity(new CellIdentityGsm(Integer.parseInt(US_MCC),
+                Integer.parseInt(FAKE_MNC), 0, 0));
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        doReturn(true).when(mSST).getDesiredPowerState();
+
+        mLocaleTrackerTestHandler = new LocaleTrackerTestHandler(getClass().getSimpleName());
+        mLocaleTrackerTestHandler.start();
+        waitUntilReady();
+        logd("LocaleTrackerTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mLocaleTracker.removeCallbacksAndMessages(null);
+        mLocaleTrackerTestHandler.quit();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateOperatorNumericSync() throws Exception {
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateOperatorNumericAsync() throws Exception {
+        mLocaleTracker.updateOperatorNumericAsync(US_MCC + FAKE_MNC);
+        waitForMs(100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+    }
+
+    @Test
+    @SmallTest
+    public void testNoSim() throws Exception {
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+    }
+
+    @Test
+    @SmallTest
+    public void testBootupInAirplaneModeOn() throws Exception {
+        doReturn(false).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
+    }
+
+    @Test
+    @SmallTest
+    public void testTogglingAirplaneMode() throws Exception {
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+
+        doReturn(false).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
+
+        doReturn(true).when(mSST).getDesiredPowerState();
+        mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE);
+    }
+
+    @Test
+    @SmallTest
+    public void testCellInfoUnavailableRetry() throws Exception {
+        doReturn(null).when(mPhone).getAllCellInfo(isNull());
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
+
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        waitForHandlerActionDelayed(mLocaleTracker, 100, 2500);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+    }
+
+    @Test
+    @SmallTest
+    public void testOutOfAirplaneMode() throws Exception {
+        doReturn(null).when(mPhone).getAllCellInfo(isNull());
+        mLocaleTracker.updateOperatorNumericAsync("");
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
+
+        doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+        AsyncResult ar = new AsyncResult(null, ss, null);
+        mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(3, ar));
+        waitForHandlerAction(mLocaleTracker, 100);
+        assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index b45c62f..f2afc96 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -57,6 +57,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
@@ -110,6 +111,7 @@
 import android.os.PowerManager;
 import android.os.WorkSource;
 import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
@@ -204,6 +206,25 @@
     private static final int TYPE_LTE = 3;
     private static final int TYPE_WCDMA = 4;
 
+    private static final int PROFILE_ID = 0;
+    private static final String APN = "apn";
+    private static final String PROTOCOL = "IPV6";
+    private static final int AUTH_TYPE = 0;
+    private static final String USER_NAME = "username";
+    private static final String PASSWORD = "password";
+    private static final int TYPE = 0;
+    private static final int MAX_CONNS_TIME = 1;
+    private static final int MAX_CONNS = 3;
+    private static final int WAIT_TIME = 10;
+    private static final boolean APN_ENABLED = true;
+    private static final int SUPPORTED_APNT_YPES_BITMAP = 123456;
+    private static final String ROAMING_PROTOCOL = "IPV6";
+    private static final int BEARER_BITMAP = 123123;
+    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 class RILTestHandler extends HandlerThread {
 
         RILTestHandler(String name) {
@@ -247,7 +268,8 @@
     }
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
+        super.setUp(RILTest.class.getSimpleName());
         MockitoAnnotations.initMocks(this);
         mTestHandler = new RILTestHandler(getClass().getSimpleName());
         mTestHandler.start();
@@ -257,6 +279,7 @@
     @After
     public void tearDown() throws Exception {
         mTestHandler.quit();
+        super.tearDown();
     }
 
     @FlakyTest
@@ -1643,4 +1666,37 @@
         assertEquals(getTdScdmaSignalStrength_1_0(-1), getTdScdmaSignalStrength_1_2(255));
     }
 
+    @Test
+    public void testSetupDataCall() throws Exception {
+
+        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);
+        mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
+                false, 0, null, obtainMessage());
+        ArgumentCaptor<DataProfileInfo> dpiCaptor = ArgumentCaptor.forClass(DataProfileInfo.class);
+        verify(mRadioProxy).setupDataCall(
+                mSerialNumberCaptor.capture(), eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+                dpiCaptor.capture(), eq(true), eq(false), eq(false));
+        verifyRILResponse(
+                mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SETUP_DATA_CALL);
+        DataProfileInfo dpi = dpiCaptor.getValue();
+        assertEquals(PROFILE_ID, dpi.profileId);
+        assertEquals(APN, dpi.apn);
+        assertEquals(PROTOCOL, dpi.protocol);
+        assertEquals(AUTH_TYPE, dpi.authType);
+        assertEquals(USER_NAME, dpi.user);
+        assertEquals(PASSWORD, dpi.password);
+        assertEquals(TYPE, dpi.type);
+        assertEquals(MAX_CONNS_TIME, dpi.maxConnsTime);
+        assertEquals(MAX_CONNS, dpi.maxConns);
+        assertEquals(WAIT_TIME, dpi.waitTime);
+        assertEquals(APN_ENABLED, dpi.enabled);
+        assertEquals(SUPPORTED_APNT_YPES_BITMAP, dpi.supportedApnTypesBitmap);
+        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/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 3108d2b..cb9f2ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -60,13 +60,16 @@
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.NetworkRegistrationState;
 import android.telephony.NetworkService;
+import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -88,6 +91,7 @@
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
@@ -1627,4 +1631,97 @@
         waitForMs(200);
         assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
     }
+
+    private void sendPhyChanConfigChange(int[] bandwidths) {
+        ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
+        int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+        for (int bw : bandwidths) {
+            pc.add(new PhysicalChannelConfig(ssType, bw));
+
+            // All cells after the first are secondary serving cells.
+            ssType = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
+        }
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_PHYSICAL_CHANNEL_CONFIG,
+                new AsyncResult(null, pc, null)));
+        waitForMs(100);
+    }
+
+    private void sendRegStateUpdateForLteCellId(CellIdentityLte cellId) {
+        NetworkRegistrationState dataResult = new NetworkRegistrationState(
+                1, 2, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1);
+        NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+                1, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId,
+                false, 0, 0, 0);
+        sst.mPollingContext[0] = 2;
+        // update data reg state to be in service
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+                new AsyncResult(sst.mPollingContext, dataResult, null)));
+        waitForMs(200);
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, voiceResult, null)));
+        waitForMs(200);
+    }
+
+    @Test
+    public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentity5 =
+                new CellIdentityLte(1, 1, 5, 1, 5000, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000});
+        sendRegStateUpdateForLteCellId(cellIdentity5);
+        assertTrue(Arrays.equals(new int[] {5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthNotUpdatedWhenInvalidInCellIdentity() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentityInv =
+                new CellIdentityLte(1, 1, 5, 1, 12345, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000});
+        sendRegStateUpdateForLteCellId(cellIdentityInv);
+        assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthPrefersCarrierAggregationReport() throws Exception {
+        // Cell ID change should trigger hasLocationChanged.
+        CellIdentityLte cellIdentity10 =
+                new CellIdentityLte(1, 1, 5, 1, 10000, "001", "01", "test", "tst");
+
+        sendPhyChanConfigChange(new int[] {10000, 5000});
+        sendRegStateUpdateForLteCellId(cellIdentity10);
+        assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
+        // LTE Cell with bandwidth = 10000
+        CellIdentityLte cellIdentity10 =
+                new CellIdentityLte(1, 1, 1, 1, 10000, "1", "1", "test", "tst");
+
+        sendRegStateUpdateForLteCellId(cellIdentity10);
+        assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+        sendPhyChanConfigChange(new int[] {10000, 5000});
+        assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+    }
+
+    @Test
+    public void testPhyChanBandwidthResetsOnOos() throws Exception {
+        testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
+        NetworkRegistrationState dataResult = new NetworkRegistrationState(
+                1, 2, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1);
+        NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+                1, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null,
+                false, 0, 0, 0);
+        sst.mPollingContext[0] = 2;
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+                new AsyncResult(sst.mPollingContext, dataResult, null)));
+        waitForMs(200);
+        sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, voiceResult, null)));
+        waitForMs(200);
+        assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 56c6e91..89d8143 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -332,7 +332,8 @@
                         nullable(CommandsInterface.class));
         doReturn(mUiccProfile).when(mTelephonyComponentFactory)
                 .makeUiccProfile(nullable(Context.class), nullable(CommandsInterface.class),
-                        nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class));
+                        nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
+                        nullable(Object.class));
         doReturn(mCT).when(mTelephonyComponentFactory)
                 .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
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 748ddf9..4c49e5b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -138,6 +138,7 @@
         assertEquals(a1.mvnoType, a2.mvnoType);
         assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
         assertEquals(a1.networkTypeBitmask, a2.networkTypeBitmask);
+        assertEquals(a1.apnSetId, a2.apnSetId);
     }
 
     @Test
@@ -230,6 +231,22 @@
                 "testspn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
+        // A v5 string with apnSetId=0
+        testString =
+                "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,0";
+        expectedApn = new ApnSetting(
+                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
+                "IP", true, 0, 0, 0, false, 0, 0, 0, 0, "spn", "testspn");
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+        // A v5 string with apnSetId=3
+        testString =
+                "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,3";
+        expectedApn = new ApnSetting(
+                -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
+                "IP", true, 0, 0, false, 0, 0, 0, 0, "spn", "testspn", 3);
+        assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
         // Return no apn if insufficient fields given.
         testString = "[ApnSettingV3] Name,apn,,,,,,,,,123, 45,,mms|*";
         assertEquals(null, ApnSetting.fromString(testString));
@@ -248,6 +265,8 @@
                 " ;[ApnSettingV3] Name1,apn1,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,gid,testGid";
         testString +=
                 " ;[ApnSettingV3] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,,";
+        testString +=
+                " ;[ApnSettingV5] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,0,,,,,,,,,,3";
         List<ApnSetting> expectedApns = new ArrayList<ApnSetting>();
         expectedApns.add(new ApnSetting(
                 -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
@@ -258,6 +277,9 @@
         expectedApns.add(new ApnSetting(
                 -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
                 "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "", ""));
+        expectedApns.add(new ApnSetting(
+                -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
+                "IP", true, 0, 0, false, 0, 0, 0, 0, "", "", 3));
         assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
     }
 
@@ -265,13 +287,25 @@
     @SmallTest
     public void testToString() throws Exception {
         String[] types = {"default", "*"};
+        // use default apn_set_id constructor
         ApnSetting apn = new ApnSetting(
                 99, "12345", "Name", "apn", "proxy", "port",
                 "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
                 types, "IPV6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
-        String expected = "[ApnSettingV4] Name, 99, 12345, apn, proxy, "
+        String expected = "[ApnSettingV5] Name, 99, 12345, apn, proxy, "
                 + "mmsc, mmsproxy, mmsport, port, 0, default | *, "
-                + "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false, 4096";
+                + "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false, 4096, 0";
+        assertEquals(expected, apn.toString());
+
+        int networkTypeBitmask = 1 << (14 - 1);
+        int bearerBitmask =
+                ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask);
+        apn = new ApnSetting(99, "12345", "Name", "apn", "proxy", "port",
+                "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
+                types, "IPV6", "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "", "", 3);
+        expected = "[ApnSettingV5] Name, 99, 12345, apn, proxy, "
+                + "mmsc, mmsproxy, mmsport, port, 0, default | *, IPV6, IP, true, 0, "
+                + bearerBitmask + ", 0, false, 0, 0, 0, 0, , , false, 8192, 3";
         assertEquals(expected, apn.toString());
     }
 
@@ -863,4 +897,4 @@
         assertTrue(apn1.equals(apn2, false));
         assertFalse(apn1.equals(apn2, true));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index d94a8b1..b2fea9b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -129,6 +129,34 @@
             "",                     // mvno_type
             "");                    // mnvo_match_data
 
+    private ApnSetting mApn2 = new ApnSetting(
+            2164,                   // id
+            "44010",                // numeric
+            "sp-mode",              // name
+            "spmode.ne.jp",         // apn
+            "",                     // proxy
+            "",                     // port
+            "",                     // mmsc
+            "",                     // mmsproxy
+            "",                     // mmsport
+            "",                     // user
+            "",                     // password
+            -1,                     // authtype
+            new String[]{"default", "dun"},     // types
+            "IP",                   // protocol
+            "IP",                   // roaming_protocol
+            true,                   // carrier_enabled
+            0,                      // bearer
+            0,                      // bearer_bitmask
+            0,                      // profile_id
+            false,                  // modem_cognitive
+            0,                      // max_conns
+            0,                      // wait_time
+            0,                      // max_conns_time
+            0,                      // mtu
+            "",                     // mvno_type
+            "");                    // mnvo_match_data
+
     private class DataConnectionTestHandler extends HandlerThread {
 
         private DataConnectionTestHandler(String name) {
@@ -174,6 +202,7 @@
                 ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
         doReturn(mApn1).when(mApnContext).getApnSetting();
         doReturn(PhoneConstants.APN_TYPE_DEFAULT).when(mApnContext).getApnType();
+        doReturn(true).when(mDcTracker).isDataEnabled();
 
         mDcFailBringUp.saveParameters(0, 0, -2);
         doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
@@ -376,6 +405,36 @@
 
     @Test
     @SmallTest
+    public void testNetworkCapability() throws Exception {
+        mContextFixture.getCarrierConfigBundle().putStringArray(
+                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[] { "default" });
+        doReturn(mApn2).when(mApnContext).getApnSetting();
+        testConnectEvent();
+
+        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+
+        mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
+        waitForMs(100);
+        doReturn(mApn1).when(mApnContext).getApnSetting();
+        mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
+        waitForMs(200);
+
+        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
+    }
+
+    @Test
+    @SmallTest
     public void testMeteredCapability() throws Exception {
 
         mContextFixture.getCarrierConfigBundle().
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 a2cb71c..89d670c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -38,6 +38,7 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -89,6 +90,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -122,6 +124,9 @@
             1 << (TelephonyManager.NETWORK_TYPE_LTE - 1);
     private static final int NETWORK_TYPE_EHRPD_BITMASK =
             1 << (TelephonyManager.NETWORK_TYPE_EHRPD - 1);
+    private static final Uri PREFERAPN_URI = Uri.parse(
+            Telephony.Carriers.CONTENT_URI + "/preferapn");
+
 
     @Mock
     ISub mIsub;
@@ -181,6 +186,7 @@
     }
 
     private class ApnSettingContentProvider extends MockContentProvider {
+        private int mPreferredApnSet = 0;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -225,7 +231,8 @@
                                     Telephony.Carriers.MAX_CONNS_TIME, Telephony.Carriers.MTU,
                                     Telephony.Carriers.MVNO_TYPE,
                                     Telephony.Carriers.MVNO_MATCH_DATA,
-                                    Telephony.Carriers.NETWORK_TYPE_BITMASK});
+                                    Telephony.Carriers.NETWORK_TYPE_BITMASK,
+                                    Telephony.Carriers.APN_SET_ID});
 
                     mc.addRow(new Object[]{
                             2163,                   // id
@@ -254,7 +261,8 @@
                             0,                      // mtu
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
-                            NETWORK_TYPE_LTE_BITMASK // network_type_bitmask
+                            NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+                            0                       // apn_set_id
                     });
 
                     mc.addRow(new Object[]{
@@ -284,7 +292,8 @@
                             0,                      // mtu
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
-                            NETWORK_TYPE_LTE_BITMASK // network_type_bitmask
+                            NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+                            0                       // apn_set_id
                     });
 
                     mc.addRow(new Object[]{
@@ -314,7 +323,8 @@
                             0,                      // mtu
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
-                            0                       // network_type_bitmask
+                            0,                      // network_type_bitmask
+                            0                       // apn_set_id
                     });
 
                     mc.addRow(new Object[]{
@@ -344,7 +354,8 @@
                             0,                      // mtu
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
-                            NETWORK_TYPE_EHRPD_BITMASK // network_type_bitmask
+                            NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
+                            0                       // apn_set_id
                     });
 
                     mc.addRow(new Object[]{
@@ -374,14 +385,30 @@
                             0,                      // mtu
                             "",                     // mvno_type
                             "",                     // mnvo_match_data
-                            0                       // network_type_bitmask
+                            0,                      // network_type_bitmask
+                            0                       // apn_set_id
                     });
+
                     return mc;
                 }
+            } else if (uri.isPathPrefixMatch(
+                    Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapnset"))) {
+                MatrixCursor mc = new MatrixCursor(
+                        new String[]{Telephony.Carriers.APN_SET_ID});
+                // apn_set_id is the only field used with this URL
+                mc.addRow(new Object[]{ mPreferredApnSet });
+                mc.addRow(new Object[]{ 0 });
+                return mc;
             }
 
             return null;
         }
+
+        @Override
+        public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+            mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
+            return 1;
+        }
     }
 
     @Before
@@ -1293,7 +1320,7 @@
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
     }
 
-// Test for fetchDunApn()
+    // Test for fetchDunApns()
     @Test
     @SmallTest
     public void testFetchDunApn() {
@@ -1308,15 +1335,48 @@
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.TETHER_DUN_APN, dunApnString);
         // should return APN from Setting
-        ApnSetting dunApn = mDct.fetchDunApn();
+        ApnSetting dunApn = mDct.fetchDunApns().get(0);
         assertTrue(dunApnExpected.equals(dunApn));
 
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.TETHER_DUN_APN, null);
         // should return APN from db
-        dunApn = mDct.fetchDunApn();
+        dunApn = mDct.fetchDunApns().get(0);
         assertEquals(FAKE_APN5, dunApn.apn);
     }
+
+    // Test for fetchDunApns() with apn set id
+    @Test
+    @SmallTest
+    public void testFetchDunApnWithPreferredApnSet() {
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        // apnSetId=1
+        String dunApnString1 = "[ApnSettingV5]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+                + "0,,,,,,,,,,1";
+        // apnSetId=0
+        String dunApnString2 = "[ApnSettingV5]HOT mobile PC,pc.coldm,,,,,,,,,440,10,,DUN,,,true,"
+                + "0,,,,,,,,,,0";
+
+        ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString1);
+
+        ContentResolver cr = mContext.getContentResolver();
+        Settings.Global.putString(cr, Settings.Global.TETHER_DUN_APN,
+                dunApnString1 + ";" + dunApnString2);
+
+        // set that we prefer apn set 1
+        ContentValues values = new ContentValues();
+        values.put(Telephony.Carriers.APN_SET_ID, 1);
+        cr.update(PREFERAPN_URI, values, null, null);
+
+        // return APN from Setting with apnSetId=1
+        ArrayList<ApnSetting> dunApns = mDct.sortApnListByPreferred(mDct.fetchDunApns());
+        assertEquals(2, dunApns.size());
+        assertTrue(dunApnExpected.equals(dunApns.get(0)));
+    }
+
     // Test oos
     @Test
     @SmallTest
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 d4c54b9..2bf0094 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -53,8 +53,12 @@
     private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
     private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
     private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
-    private static final int WFC_IMS_MODE_DEFAULT_VAL = 2;
-    private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL = 3;
+    private static final boolean WFC_IMS_EDITABLE_VAL = true;
+    private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
+    private static final int WFC_IMS_MODE_DEFAULT_VAL =
+            ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
+    private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
+            ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
 
     PersistableBundle mBundle;
     @Mock IBinder mBinder;
@@ -62,7 +66,6 @@
     Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
     Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
     ImsConfigImplBase.ImsConfigStub mImsConfigStub;
-    ImsConfig mImsConfig;
     @Mock MmTelFeatureConnection mMmTelFeatureConnection;
 
     private final int[] mSubId = {0};
@@ -94,6 +97,8 @@
     private void setDefaultValues() {
         mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
                 ENHANCED_4G_MODE_EDITABLE);
+        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+                WFC_IMS_EDITABLE_VAL);
         mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
                 WFC_IMS_ENABLE_DEFAULT_VAL);
         mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
@@ -239,14 +244,22 @@
 
     }
 
+    /**
+     * Tests that when a WFC mode is set for home/roaming, that setting is sent to the ImsService
+     * correctly.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+     */
     @Test @SmallTest
     public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
-        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
+        // First, Set WFC home/roaming mode that is not the Carrier Config default.
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
                 .when(mSubscriptionController).getSubscriptionProperty(
                         anyInt(),
                         eq(SubscriptionManager.WFC_IMS_MODE),
                         anyString());
-        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
                 .when(mSubscriptionController).getSubscriptionProperty(
                         anyInt(),
                         eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
@@ -257,22 +270,100 @@
         doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
         // Turn on WFC
         imsManager.setWfcSetting(true);
-        // Roaming mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
+        // Roaming mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
         verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
                 eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
-                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
+                eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
 
         // Not roaming
         doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
         // Turn on WFC
         imsManager.setWfcSetting(true);
-        // Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+        // Home mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
         verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
                 eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
-                eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
+                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
     }
 
-    private ImsManager initializeProvisionedValues() {
+    /**
+     * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
+     * editable.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
+     */
+    @Test @SmallTest
+    public void testSetWfcSetting_wfcNotEditable() throws Exception {
+        mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+                WFC_IMS_NOT_EDITABLE_VAL);
+        // Set some values that are different than the defaults for WFC mode.
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                anyInt(),
+                eq(SubscriptionManager.WFC_IMS_MODE),
+                anyString());
+        doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+                .when(mSubscriptionController).getSubscriptionProperty(
+                anyInt(),
+                eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+                anyString());
+        ImsManager imsManager = initializeProvisionedValues();
+
+        // Roaming
+        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
+        // not WFC mode is editable. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+
+        // Not roaming
+        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+        // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_MODE_DEFAULT_VAL));
+    }
+
+    /**
+     * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
+     * Manager.
+     *
+     * Preconditions:
+     *  - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
+     *  - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
+     */
+    @Test @SmallTest
+    public void testSetWfcSetting_noUserSettingSet() throws Exception {
+        ImsManager imsManager = initializeProvisionedValues();
+
+        // Roaming
+        doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+
+        // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
+
+        // Not roaming
+        doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+        // Turn on WFC
+        imsManager.setWfcSetting(true);
+
+        // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
+        verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+                eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+                eq(WFC_IMS_MODE_DEFAULT_VAL));
+    }
+
+    private ImsManager initializeProvisionedValues() throws Exception {
         when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
                 .thenAnswer(invocation ->  {
                     return getProvisionedInt((Integer) (invocation.getArguments()[0]));
@@ -288,15 +379,11 @@
 
         // Configure ImsConfigStub
         mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
-        doReturn(mImsConfigStub).when(mImsConfigImplBaseMock).getIImsConfig();
-
-        // Configure ImsConfig
-        mImsConfig = new ImsConfig(mImsConfigStub, mContext);
+        doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
 
         // Configure ImsManager
         ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
         try {
-            replaceInstance(ImsManager.class, "mConfig", imsManager, mImsConfig);
             replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
                     mMmTelFeatureConnection);
         } catch (Exception ex) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index dc21b70..27b8531 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -63,7 +63,8 @@
         @Override
         public void onLooperPrepared() {
             mUicccard = new UiccCard(mContextFixture.getTestDouble(),
-                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
+                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
+                                     new Object());
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
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 8b87e9f..0ae5531 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -84,7 +84,7 @@
         public void onLooperPrepared() {
             mUiccProfile = new UiccProfile(mContextFixture.getTestDouble(),
                                            mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
-                                           mUiccCard);
+                                           mUiccCard, new Object());
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index c046980..457f021 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -102,8 +102,9 @@
 
     @Test
     @SmallTest
-    public void testUpdateSlotStatus() {
+    public void testUpdateInactiveSlotStatus() {
         IccSlotStatus iss = new IccSlotStatus();
+        iss.logicalSlotIndex = 0;
         iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
         iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
         iss.iccid = "fake-iccid";
@@ -122,8 +123,35 @@
         assertNull(mUiccSlot.getUiccCard());
         assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
         assertEquals(iss.iccid, mUiccSlot.getIccId());
+    }
 
+    @Test
+    @SmallTest
+    public void testUpdateActiveSlotStatus() {
+        // initial state
+        assertTrue(mUiccSlot.isActive());
+        assertNull(mUiccSlot.getUiccCard());
+        assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+        assertNull(mUiccSlot.getIccId());
+
+        mSimulatedCommands.setRadioPower(true, null);
+        int phoneId = 0;
+        IccSlotStatus iss = new IccSlotStatus();
+        iss.logicalSlotIndex = phoneId;
         iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_ACTIVE;
+        iss.cardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+        iss.iccid = "fake-iccid";
+
+        // update slot to inactive
+        mUiccSlot.update(mSimulatedCommands, iss);
+
+        // assert on updated values
+        assertTrue(mUiccSlot.isActive());
+        assertNull(mUiccSlot.getUiccCard());
+        assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+        assertEquals(iss.iccid, mUiccSlot.getIccId());
+        verify(mSubInfoRecordUpdater).updateInternalIccState(
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
 
         // update slot to active
         mUiccSlot.update(mSimulatedCommands, iss);
@@ -136,6 +164,7 @@
     @SmallTest
     public void testUpdateSlotStatusEuiccIsSupported() {
         IccSlotStatus iss = new IccSlotStatus();
+        iss.logicalSlotIndex = 0;
         iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
         iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
         iss.iccid = "fake-iccid";
@@ -170,6 +199,7 @@
     @SmallTest
     public void testUpdateSlotStatusEuiccIsNotSupported() {
         IccSlotStatus iss = new IccSlotStatus();
+        iss.logicalSlotIndex = 0;
         iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
         iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
         iss.iccid = "fake-iccid";
@@ -221,7 +251,8 @@
         mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
         verify(mTelephonyComponentFactory).makeUiccProfile(
-                anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject());
+                anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
+                anyObject());
         assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
         assertNotNull(mUiccSlot.getUiccCard());
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index a90e947..f933596 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -101,7 +101,7 @@
 
         // The first broadcast should be sent after initialization.
         UiccCard card = new UiccCard(mContext, mSimulatedCommands,
-                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
+                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object());
         when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
         uiccLauncher.handleMessage(msg);
 
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 801ef86..a3d7245 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
@@ -103,7 +103,7 @@
         public void onLooperPrepared() {
             mEuiccCard =
                     new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus,
-                            0 /* phoneId */) {
+                            0 /* phoneId */, new Object()) {
                         @Override
                         protected byte[] getDeviceId() {
                             return IccUtils.bcdToBytes("987654321012345");
@@ -173,7 +173,7 @@
             final CountDownLatch latch = new CountDownLatch(1);
             mHandler.post(() -> {
                 mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
-                        mMockIccCardStatus, 0 /* phoneId */);
+                        mMockIccCardStatus, 0 /* phoneId */, new Object());
                 latch.countDown();
             });
             assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));