Merge "Remove duplicate new TelephonyNetworkFactory[#]"
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 4ff05e1..73ec639 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -234,7 +234,7 @@
                             mIsImsServiceUp = false;
                         }
                     }
-                });
+                }, "ImsSmsDispatcher");
         mImsManagerConnector.connect();
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 0861cf5..8683406 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -243,7 +243,7 @@
                     Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
                     sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
                             isDynamicBinding);
-                    sImsResolver.initPopulateCacheAndStartBind();
+                    sImsResolver.initialize();
                     // Start monitoring after defaults have been made.
                     // Default phone must be ready before ImsPhone is created because ImsService
                     // might need it when it is being opened. This should initialize multiple
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index cb95ee6..839210f 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -2385,16 +2385,16 @@
              * is set to roaming when either is true.
              *
              * There are exceptions for the above rule.
-             * The new SS is not set as roaming while gsm service reports
-             * roaming but indeed it is same operator.
-             * And the operator is considered non roaming.
+             * The new SS is not set as roaming while gsm service or
+             * data service reports roaming but indeed it is same
+             * operator. And the operator is considered non roaming.
              *
              * The test for the operators is to handle special roaming
              * agreements and MVNO's.
              */
             boolean roaming = (mGsmRoaming || mDataRoaming);
 
-            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS)
+            if (roaming && !isOperatorConsideredRoaming(mNewSS)
                     && (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
                 log("updateRoamingState: resource override set non roaming.isSameNamedOperators="
                         + isSameNamedOperators(mNewSS) + ",isOperatorConsideredNonRoaming="
@@ -3101,6 +3101,11 @@
             }
         }
 
+        // Filter out per transport data RAT changes, only want to track changes based on
+        // transport preference changes (WWAN to WLAN, for example).
+        boolean hasDataTransportPreferenceChanged = !anyDataRatChanged
+                && (mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology());
+
         boolean hasVoiceRegStateChanged =
                 mSS.getVoiceRegState() != mNewSS.getVoiceRegState();
 
@@ -3180,6 +3185,7 @@
                     + " hasDataRegStateChanged = " + hasDataRegStateChanged
                     + " hasRilVoiceRadioTechnologyChanged = " + hasRilVoiceRadioTechnologyChanged
                     + " hasRilDataRadioTechnologyChanged = " + hasRilDataRadioTechnologyChanged
+                    + " hasDataTransportPreferenceChanged = " + hasDataTransportPreferenceChanged
                     + " hasChanged = " + hasChanged
                     + " hasVoiceRoamingOn = " + hasVoiceRoamingOn
                     + " hasVoiceRoamingOff = " + hasVoiceRoamingOff
@@ -3355,7 +3361,10 @@
             }
 
             if (hasDataRegStateChanged.get(transport)
-                    || hasRilDataRadioTechnologyChanged.get(transport)) {
+                    || hasRilDataRadioTechnologyChanged.get(transport)
+                    // Update all transports if preference changed so that consumers can be notified
+                    // that ServiceState#getRilDataRadioTechnology has changed.
+                    || hasDataTransportPreferenceChanged) {
                 notifyDataRegStateRilRadioTechnologyChanged(transport);
                 mPhone.notifyDataConnection();
             }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
index 4987e51..1766d05 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
@@ -184,6 +184,7 @@
 
         public CdmaNumberInfoRec(int id, String number, int numberType, int numberPlan, int pi,
                 int si) {
+            this.id = id;
             this.number = number;
             this.numberType = (byte)numberType;
             this.numberPlan = (byte)numberPlan;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 7606073..78f7779 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.KeepalivePacketData;
 import android.net.LinkAddress;
@@ -38,11 +39,13 @@
 import android.net.StringNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Telephony;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DataFailCause;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.Rlog;
@@ -50,11 +53,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
 import android.text.TextUtils;
+import android.util.LocalLog;
 import android.util.Pair;
 import android.util.StatsLog;
 import android.util.TimeUtils;
@@ -174,6 +179,8 @@
 
     private final String mTagSuffix;
 
+    private final LocalLog mHandoverLocalLog = new LocalLog(100);
+
     /**
      * Used internally for saving connecting parameters.
      */
@@ -680,6 +687,7 @@
             linkProperties = dc.getLinkProperties();
             // Preserve the potential network agent from the source data connection. The ownership
             // is not transferred at this moment.
+            mHandoverLocalLog.log("Handover started. Preserved the agent.");
             mHandoverSourceNetworkAgent = dc.getNetworkAgent();
             log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
             dc.setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
@@ -1131,14 +1139,28 @@
      * @return True if this data connection should only be used for unmetered purposes.
      */
     private boolean isUnmeteredUseOnly() {
-        // The data connection can only be unmetered used only if all requests' reasons are
-        // unmetered.
+        // If this data connection is on IWLAN, then it's unmetered and can be used by everyone.
+        // Should not be for unmetered used only.
+        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+            return false;
+        }
+
+        // If data is enabled, this data connection can't be for unmetered used only because
+        // everyone should be able to use it.
+        if (mPhone.getDataEnabledSettings().isDataEnabled()) {
+            return false;
+        }
+
+        // If the device is roaming and data roaming it turned on, then this data connection can't
+        // be for unmetered use only.
+        if (mDct.getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
+            return false;
+        }
+
+        // The data connection can only be unmetered used only if all attached APN contexts
+        // attached to this data connection are unmetered.
         for (ApnContext apnContext : mApnContexts.keySet()) {
-            DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
-            boolean isDataAllowed = mDct.isDataAllowed(apnContext, DcTracker.REQUEST_TYPE_NORMAL,
-                    dataConnectionReasons);
-            if (!isDataAllowed || !dataConnectionReasons.contains(
-                    DataConnectionReasons.DataAllowedReasonType.UNMETERED_APN)) {
+            if (ApnSettingUtils.isMeteredApnType(apnContext.getApnTypeBitmask(), mPhone)) {
                 return false;
             }
         }
@@ -1667,6 +1689,31 @@
                 mHandoverState = HANDOVER_STATE_COMPLETED;
             }
 
+            // Check for dangling agent. Ideally the handover source agent should be null if
+            // handover process is smooth. When it's not null, that means handover failed. The
+            // agent was not successfully transferred to the new data connection. We should
+            // gracefully notify connectivity service the network was disconnected.
+            if (mHandoverSourceNetworkAgent != null) {
+                DataConnection sourceDc = mHandoverSourceNetworkAgent.getDataConnection();
+                if (sourceDc != null) {
+                    // If the source data connection still owns this agent, then just reset the
+                    // handover state back to idle because handover is already failed.
+                    mHandoverLocalLog.log(
+                            "Handover failed. Reset the source dc state to idle");
+                    sourceDc.setHandoverState(HANDOVER_STATE_IDLE);
+                } else {
+                    // The agent is now a dangling agent. No data connection owns this agent.
+                    // Gracefully notify connectivity service disconnected.
+                    mHandoverLocalLog.log(
+                            "Handover failed and dangling agent found.");
+                    mHandoverSourceNetworkAgent.acquireOwnership(
+                            DataConnection.this, mTransportType);
+                    mHandoverSourceNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
+                    mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this);
+                }
+                mHandoverSourceNetworkAgent = null;
+            }
+
             if (mConnectionParams != null) {
                 if (DBG) {
                     log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
@@ -1941,15 +1988,25 @@
                 }
 
                 if (mHandoverSourceNetworkAgent != null) {
-                    log("Transfer network agent successfully.");
+                    String logStr = "Transfer network agent successfully.";
+                    log(logStr);
+                    mHandoverLocalLog.log(logStr);
                     mNetworkAgent = mHandoverSourceNetworkAgent;
                     mNetworkAgent.acquireOwnership(DataConnection.this, mTransportType);
+
+                    // TODO: Should evaluate mDisabledApnTypeBitMask again after handover. We don't
+                    // do it now because connectivity service does not support dynamically removing
+                    // immutable capabilities.
+
+                    // Update the capability after handover
                     mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
                             DataConnection.this);
                     mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
                     mHandoverSourceNetworkAgent = null;
                 } else {
-                    loge("Failed to get network agent from original data connection");
+                    String logStr = "Failed to get network agent from original data connection";
+                    loge(logStr);
+                    mHandoverLocalLog.log(logStr);
                     return;
                 }
             } else {
@@ -1958,6 +2015,9 @@
                         mPhone.getPhoneId());
                 final int factorySerialNumber = (null == factory)
                         ? NetworkFactory.SerialNumber.NONE : factory.getSerialNumber();
+
+                mDisabledApnTypeBitMask |= getDisallowedApnTypes();
+
                 mNetworkAgent = DcNetworkAgent.createDcNetworkAgent(DataConnection.this,
                         mPhone, mNetworkInfo, mScore, misc, factorySerialNumber, mTransportType);
             }
@@ -2562,6 +2622,8 @@
     }
 
     void setHandoverState(@HandoverState int state) {
+        mHandoverLocalLog.log("State changed from " + handoverStateToString(mHandoverState)
+                + " to " + handoverStateToString(state));
         mHandoverState = state;
     }
 
@@ -2713,6 +2775,33 @@
                 == NetworkRegistrationInfo.NR_STATE_CONNECTED;
     }
 
+    /**
+     * @return The disallowed APN types bitmask
+     */
+    private @ApnType int getDisallowedApnTypes() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        int apnTypesBitmask = 0;
+        if (configManager != null) {
+            PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
+            if (bundle != null) {
+                String key = (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                        ? CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY
+                        : CarrierConfigManager.KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY;
+                if (bundle.getStringArray(key) != null) {
+                    String disallowedApnTypesString =
+                            TextUtils.join(",", bundle.getStringArray(key));
+                    if (!TextUtils.isEmpty(disallowedApnTypesString)) {
+                        apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString(
+                                disallowedApnTypesString);
+                    }
+                }
+            }
+        }
+
+        return apnTypesBitmask;
+    }
+
     private void dumpToLog() {
         dump(null, new PrintWriter(new StringWriter(0)) {
             @Override
@@ -2758,6 +2847,15 @@
         return score;
     }
 
+    private String handoverStateToString(@HandoverState int state) {
+        switch (state) {
+            case HANDOVER_STATE_IDLE: return "IDLE";
+            case HANDOVER_STATE_BEING_TRANSFERRED: return "BEING_TRANSFERRED";
+            case HANDOVER_STATE_COMPLETED: return "COMPLETED";
+            default: return "UNKNOWN";
+        }
+    }
+
     /**
      * Dump the current state.
      *
@@ -2787,7 +2885,7 @@
         pw.println("mLinkProperties=" + mLinkProperties);
         pw.flush();
         pw.println("mDataRegState=" + mDataRegState);
-        pw.println("mHandoverState=" + mHandoverState);
+        pw.println("mHandoverState=" + handoverStateToString(mHandoverState));
         pw.println("mRilRat=" + mRilRat);
         pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
         pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
@@ -2797,12 +2895,18 @@
         pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride));
         pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride);
         pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
+        pw.println("disallowedApnTypes="
+                + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes()));
         pw.println("mInstanceNumber=" + mInstanceNumber);
         pw.println("mAc=" + mAc);
         pw.println("mScore=" + mScore);
         if (mNetworkAgent != null) {
             mNetworkAgent.dump(fd, pw, args);
         }
+        pw.println("handover local log:");
+        pw.increaseIndent();
+        mHandoverLocalLog.dump(fd, pw, args);
+        pw.decreaseIndent();
         pw.decreaseIndent();
         pw.println();
         pw.flush();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
index e7afdff..4b17215 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -115,7 +115,10 @@
         RADIO_DISABLED_BY_CARRIER(true),
         APN_NOT_CONNECTABLE(true),
         ON_IWLAN(true),
-        IN_ECBM(true);
+        IN_ECBM(true),
+        ON_OTHER_TRANSPORT(true);   // When data retry occurs, the given APN type's preferred
+                                    // transport might be already changed. In this case we
+                                    // should disallow data retry.
 
         private boolean mIsHardReason;
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
index b36a490..d9edd28 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
@@ -132,6 +132,13 @@
         mDataConnection = null;
     }
 
+    /**
+     * @return The data connection that owns this agent
+     */
+    public synchronized DataConnection getDataConnection() {
+        return mDataConnection;
+    }
+
     @Override
     protected synchronized void unwanted() {
         if (mDataConnection == null) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index cc4c90e..8efddec 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -1350,6 +1350,16 @@
             reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
         }
 
+        if (apnContext != null) {
+            // If the transport has been already switched to the other transport, we should not
+            // allow the data setup. The only exception is the handover case, where we setup
+            // handover data connection before switching the transport.
+            if (mTransportType != mPhone.getTransportManager().getCurrentTransport(
+                    apnContext.getApnTypeBitmask()) && requestType != REQUEST_TYPE_HANDOVER) {
+                reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
+            }
+        }
+
         boolean isDataEnabled = apnContext == null ? mDataEnabledSettings.isDataEnabled()
                 : mDataEnabledSettings.isDataEnabled(apnContext.getApnTypeBitmask());
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
index ea11223..43275ec 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
@@ -60,7 +60,7 @@
  *
  * The device can operate in the following modes, which is stored in the system properties
  * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to
- * IRadio version. For 1.4 or above, it's legacy mode. For 1.3 or below, it's
+ * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode.
  *
  * Legacy mode:
  *      Frameworks send all data requests to the default data service, which is the cellular data
@@ -75,13 +75,13 @@
  *      frameworks to bind.
  *
  *      Package name of data service:
- *          The resource overlay 'config_wwan_data_service_package' or,
+ *          The resource overlay 'config_wlan_data_service_package' or,
  *          the carrier config
  *          {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
  *          The carrier config takes precedence over the resource overlay if both exist.
  *
  *      Package name of network service
- *          The resource overlay 'config_wwan_network_service_package' or
+ *          The resource overlay 'config_wlan_network_service_package' or
  *          the carrier config
  *          {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}.
  *          The carrier config takes precedence over the resource overlay if both exist.
@@ -129,8 +129,8 @@
     public @interface IwlanOperationMode {}
 
     /**
-     * IWLAN default mode. On device that has IRadio 1.3 or above, it means
-     * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.2 or below, it means
+     * IWLAN default mode. On device that has IRadio 1.4 or above, it means
+     * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.3 or below, it means
      * {@link #IWLAN_OPERATION_MODE_LEGACY}.
      */
     public static final String IWLAN_OPERATION_MODE_DEFAULT = "default";
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 2c822ba..a1a7aea 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -41,6 +41,7 @@
 import com.android.internal.telephony.LocaleTracker;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -80,6 +81,11 @@
     private final CommandsInterface mCi;
     private final Phone mPhone;
     private String mCountryIso;
+    /**
+     * Indicates if the country iso is set by another subscription.
+     * @hide
+     */
+    public boolean mIsCountrySetByAnotherSub = false;
     private String[] mEmergencyNumberPrefix = new String[0];
 
     private static final String EMERGENCY_NUMBER_DB_ASSETS_FILE = "eccdata";
@@ -131,7 +137,9 @@
                     if (TextUtils.isEmpty(countryIso)) {
                         return;
                     }
-                    updateEmergencyNumberDatabaseCountryChange(countryIso);
+
+                    // Update country iso change for available Phones
+                    updateEmergencyCountryIsoAllPhones(countryIso);
                 }
                 return;
             }
@@ -222,11 +230,40 @@
         // If country iso has been cached when listener is set, don't need to cache the initial
         // country iso and initial database.
         if (mCountryIso == null) {
-            mCountryIso = getInitialCountryIso().toLowerCase();
+            updateEmergencyCountryIso(getInitialCountryIso().toLowerCase());
             cacheEmergencyDatabaseByCountry(mCountryIso);
         }
     }
 
+    /**
+     * Update Emergency country iso for all the Phones
+     */
+    @VisibleForTesting
+    public void updateEmergencyCountryIsoAllPhones(String countryIso) {
+        // Notify country iso change for current Phone
+        mIsCountrySetByAnotherSub = false;
+        updateEmergencyNumberDatabaseCountryChange(countryIso);
+
+        // Share and notify country iso change for other Phones if the country
+        // iso in their emergency number tracker is not available or the country
+        // iso there is set by another active subscription.
+        for (Phone phone: PhoneFactory.getPhones()) {
+            if (phone.getPhoneId() == mPhone.getPhoneId()) {
+                continue;
+            }
+            EmergencyNumberTracker emergencyNumberTracker;
+            if (phone != null && phone.getEmergencyNumberTracker() != null) {
+                emergencyNumberTracker = phone.getEmergencyNumberTracker();
+                if (TextUtils.isEmpty(emergencyNumberTracker.getEmergencyCountryIso())
+                        || emergencyNumberTracker.mIsCountrySetByAnotherSub) {
+                    emergencyNumberTracker.mIsCountrySetByAnotherSub = true;
+                    emergencyNumberTracker.updateEmergencyNumberDatabaseCountryChange(
+                            countryIso);
+                }
+            }
+        }
+    }
+
     private void onCarrierConfigChanged() {
         if (mPhone != null) {
             CarrierConfigManager configMgr = (CarrierConfigManager)
@@ -381,8 +418,7 @@
     private void updateEmergencyNumberListDatabaseAndNotify(String countryIso) {
         logd("updateEmergencyNumberListDatabaseAndNotify(): receiving countryIso: "
                 + countryIso);
-
-        mCountryIso = countryIso.toLowerCase();
+        updateEmergencyCountryIso(countryIso.toLowerCase());
         cacheEmergencyDatabaseByCountry(countryIso);
         writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase);
         if (!DBG) {
@@ -560,6 +596,14 @@
         return EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
     }
 
+    public String getEmergencyCountryIso() {
+        return mCountryIso;
+    }
+
+    private synchronized void updateEmergencyCountryIso(String countryIso) {
+        mCountryIso = countryIso;
+    }
+
     /**
      * Get Emergency number list based on EccList. This util is used for solving backward
      * compatibility if device does not support the 1.4 IRadioIndication HAL that reports
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index c4dd115..da005fb 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -97,6 +97,9 @@
     private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
     // Testing: Overrides the current configuration for ImsService binding
     private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
+    // Based on boot complete indication. When this happens, there may be ImsServices that are not
+    // direct boot aware that need to be started.
+    private static final int HANDLER_BOOT_COMPLETE = 6;
 
     // Delay between dynamic ImsService queries.
     private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
@@ -173,7 +176,7 @@
                 res.append("(");
                 res.append(feature.slotId);
                 res.append(",");
-                res.append(feature.featureType);
+                res.append(ImsFeature.FEATURE_LOG_MAP.get(feature.featureType));
                 res.append(") ");
             }
             return res.toString();
@@ -224,6 +227,18 @@
         }
     };
 
+    // Receives the broadcast that the device has finished booting (and the device is no longer
+    // encrypted).
+    private BroadcastReceiver mBootCompleted = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "Received BOOT_COMPLETED");
+            // Recalculate all cached services to pick up ones that have just been enabled since
+            // boot complete.
+            mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
+        }
+    };
+
     /**
      * Testing interface used to mock SubscriptionManager in testing
      */
@@ -352,6 +367,11 @@
                 maybeRemovedImsService(packageName);
                 break;
             }
+            case HANDLER_BOOT_COMPLETE: {
+                // Re-evaluate bound services for all slots after requerying packagemanager
+                maybeAddedImsService(null);
+                break;
+            }
             case HANDLER_CONFIG_CHANGED: {
                 int slotId = (Integer) msg.obj;
                 carrierConfigChanged(slotId);
@@ -379,7 +399,7 @@
                 if (isCarrierImsService) {
                     Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
                             + packageName);
-                    maybeRebindService(slotId, packageName);
+                    overrideService(slotId, packageName);
                 } else {
                     Log.i(TAG, "overriding device ImsService -  packageName=" + packageName);
                     if (TextUtils.equals(mDeviceService, packageName)) {
@@ -417,8 +437,7 @@
                 @Override
                 public void onComplete(ComponentName name,
                         Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
-                    Log.d(TAG, "onComplete called for name: " + name + "features:"
-                            + printFeatures(features));
+                    Log.d(TAG, "onComplete called for name: " + name + printFeatures(features));
                     handleFeaturesChanged(name, features);
                 }
 
@@ -472,6 +491,8 @@
 
             context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
                     CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+            context.registerReceiver(mBootCompleted, new IntentFilter(
+                    Intent.ACTION_BOOT_COMPLETED));
         }
     }
 
@@ -496,10 +517,9 @@
     }
 
     /**
-     * Needs to be called after the constructor to first populate the cache and possibly bind to
-     * ImsServices.
+     * Needs to be called after the constructor to kick off the process of binding to ImsServices.
      */
-    public void initPopulateCacheAndStartBind() {
+    public void initialize() {
         Log.i(TAG, "Initializing cache and binding.");
         mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
         // Populates the CarrierConfig override package names for each slot
@@ -678,7 +698,8 @@
                 mBoundImsServicesByFeature.add(slotId, services);
             }
             Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
-                    + feature + " using package: " + controller.getComponentName());
+                    + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: "
+                    + controller.getComponentName());
             services.put(feature, controller);
         }
     }
@@ -698,7 +719,8 @@
             ImsServiceController c = services.get(feature, null);
             if (c != null) {
                 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: "
-                        + feature + " using package: " + c.getComponentName());
+                        + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: "
+                        + c.getComponentName());
                 services.remove(feature);
             }
             return c;
@@ -999,10 +1021,9 @@
         return bindableFeatures > 0;
     }
 
-    // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
-    // the SIM card has changed.
+    // Possibly rebind to another ImsService for testing.
     // Called from the handler ONLY
-    private void maybeRebindService(int slotId, String newPackageName) {
+    private void overrideService(int slotId, String newPackageName) {
         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             // not specified, replace package on all slots.
             for (int i = 0; i < mNumSlots; i++) {
@@ -1014,18 +1035,29 @@
 
     }
 
+    // Called from handler ONLY.
     private void carrierConfigChanged(int slotId) {
-        int subId = mSubscriptionManagerProxy.getSubId(slotId);
-        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
-        if (config != null) {
-            String newPackageName = config.getString(
-                    CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
-            maybeRebindService(slotId, newPackageName);
-        } else {
-            Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!");
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            // not specified, update carrier override cache and possibly rebind on all slots.
+            for (int i = 0; i < mNumSlots; i++) {
+                updateBoundCarrierServices(i, getImsPackageOverrideConfig(i));
+            }
         }
+        updateBoundCarrierServices(slotId, getImsPackageOverrideConfig(slotId));
     }
 
+    private String getImsPackageOverrideConfig(int slotId) {
+        int subId = mSubscriptionManagerProxy.getSubId(slotId);
+        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+        if (config == null) return null;
+        return config.getString(CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+    }
+
+    /**
+     * Use the slotId specified to update the carrier override cache with the new package name.
+     * If it has changed, trigger an unbind from the old service kick off the process to recalculate
+     * features supported on the new service.
+     */
     private void updateBoundCarrierServices(int slotId, String newPackageName) {
         if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
             String oldPackageName = mCarrierServices[slotId];
@@ -1136,13 +1168,13 @@
 
     private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         StringBuilder featureString = new StringBuilder();
-        featureString.append("features: [");
+        featureString.append(" features: [");
         if (features != null) {
             for (ImsFeatureConfiguration.FeatureSlotPair feature : features) {
                 featureString.append("{");
                 featureString.append(feature.slotId);
                 featureString.append(",");
-                featureString.append(feature.featureType);
+                featureString.append(ImsFeature.FEATURE_LOG_MAP.get(feature.featureType));
                 featureString.append("} ");
             }
             featureString.append("]");
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index fbfa3b0..4b1295c 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -246,7 +246,8 @@
             @Override
             public void notifyImsFeatureStatus(int featureStatus) throws RemoteException {
                 Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature="
-                        + mFeatureType + ", status=" + featureStatus);
+                        + ImsFeature.FEATURE_LOG_MAP.get(mFeatureType) + ", status="
+                        + ImsFeature.STATE_LOG_MAP.get(featureStatus));
                 sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus);
             }
         };
@@ -692,7 +693,8 @@
             } catch (RemoteException e) {
                 // The connection to this ImsService doesn't exist. This may happen if the service
                 // has died and we are removing features.
-                Log.i(LOG_TAG, "Couldn't remove feature {" + featurePair.featureType
+                Log.i(LOG_TAG, "Couldn't remove feature {"
+                        + ImsFeature.FEATURE_LOG_MAP.get(featurePair.featureType)
                         + "}, connection is down: " + e.getMessage());
             }
         } else {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 9fb8d82..86aff02 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -566,7 +566,7 @@
                     public void connectionUnavailable() {
                         stopListeningForCalls();
                     }
-                }, executor);
+                }, executor, "ImsPhoneCallTracker");
         mImsManagerConnector.connect();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index c3adcc6..b7d2913 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -29,11 +29,28 @@
 
 import androidx.test.filters.FlakyTest;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
 public class PhoneNumberUtilsTest {
 
+    private static final int MIN_MATCH = 7;
+
+    private int mOldMinMatch;
+
+    @Before
+    public void setUp() throws Exception {
+        mOldMinMatch = PhoneNumberUtils.getMinMatchForTest();
+        PhoneNumberUtils.setMinMatchForTest(MIN_MATCH);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        PhoneNumberUtils.setMinMatchForTest(mOldMinMatch);
+    }
+
     @SmallTest
     @Test
     public void testExtractNetworkPortion() throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index e4c7c11..cfd36c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -1756,6 +1756,44 @@
         waitForMs(200);
     }
 
+    private void changeRegStateWithIwlan(int state, CellIdentity cid, int voiceRat, int dataRat,
+            int iwlanState, int iwlanDataRat) {
+        LteVopsSupportInfo lteVopsSupportInfo =
+                new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                        LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+        sst.mPollingContext[0] = 3;
+
+        // PS WWAN
+        NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                state, dataRat, 0, false,
+                null, cid, 1, false, false, false, lteVopsSupportInfo, false);
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, dataResult, null)));
+        waitForMs(200);
+
+        // CS WWAN
+        NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                state, voiceRat, 0, false,
+                null, cid, false, 0, 0, 0);
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, voiceResult, null)));
+        waitForMs(200);
+
+        // PS WLAN
+        NetworkRegistrationInfo dataIwlanResult = new NetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+                iwlanState, iwlanDataRat, 0, false,
+                null, null, 1, false, false, false, lteVopsSupportInfo, false);
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_PS_IWLAN_REGISTRATION,
+                new AsyncResult(sst.mPollingContext, dataIwlanResult, null)));
+        waitForMs(200);
+    }
+
     // Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
     // Expect no rat update when move from E to G.
     @Test
@@ -1928,6 +1966,49 @@
         assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
     }
 
+    /**
+     * Ensure that TransportManager changes due to transport preference changes are picked up in the
+     * new ServiceState when a poll event occurs. This causes ServiceState#getRilDataRadioTechnology
+     * to change even though the underlying transports have not changed state.
+     */
+    @SmallTest
+    @Test
+    public void testRilDataTechnologyChangeTransportPreference() {
+        when(mTransportManager.isAnyApnPreferredOnIwlan()).thenReturn(false);
+
+        // Start state: Cell data only LTE + IWLAN
+        CellIdentityLte cellIdentity =
+                new CellIdentityLte(1, 1, 5, 1, 5000, "001", "01", "test", "tst");
+        changeRegStateWithIwlan(
+                // WWAN
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME, cellIdentity,
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, TelephonyManager.NETWORK_TYPE_LTE,
+                // WLAN
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
+        assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_LTE, sst.mSS.getRilDataRadioTechnology());
+
+        sst.registerForDataRegStateOrRatChanged(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                mTestHandler, EVENT_DATA_RAT_CHANGED, null);
+        // transport preference change for a PDN for IWLAN occurred, no registration change, but
+        // trigger unrelated poll to pick up transport preference.
+        when(mTransportManager.isAnyApnPreferredOnIwlan()).thenReturn(true);
+        changeRegStateWithIwlan(
+                // WWAN
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME, cellIdentity,
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, TelephonyManager.NETWORK_TYPE_LTE,
+                // WLAN
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+                TelephonyManager.NETWORK_TYPE_IWLAN);
+        // Now check to make sure a transport independent notification occurred for the registrants.
+        // There will be two, one when the registration happened and another when the transport
+        // preference changed.
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler, times(2)).sendMessageAtTime(messageArgumentCaptor.capture(),
+                anyLong());
+        assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, sst.mSS.getRilDataRadioTechnology());
+    }
+
     @Test
     public void testGetServiceProviderNameWithBrandOverride() {
         String brandOverride = "spn from brand override";
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 b7fe4ca..018747e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -343,6 +343,12 @@
         return (long) method.invoke(mDc, response);
     }
 
+    private boolean isUnmeteredUseOnly() throws Exception {
+        Method method = DataConnection.class.getDeclaredMethod("isUnmeteredUseOnly");
+        method.setAccessible(true);
+        return (boolean) method.invoke(mDc);
+    }
+
     private SetupResult setLinkProperties(DataCallResponse response,
                                                          LinkProperties linkProperties)
             throws Exception {
@@ -500,6 +506,12 @@
         return (NetworkCapabilities) method.invoke(mDc);
     }
 
+    private int getDisallowedApnTypes() throws Exception {
+        Method method = DataConnection.class.getDeclaredMethod("getDisallowedApnTypes");
+        method.setAccessible(true);
+        return (int) method.invoke(mDc);
+    }
+
     @Test
     @SmallTest
     public void testNetworkCapability() throws Exception {
@@ -516,6 +528,10 @@
         assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
 
+        mContextFixture.getCarrierConfigBundle().putStringArray(
+                CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+                new String[] {"supl"});
+
         mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
         waitForMs(100);
         doReturn(mApn1).when(mApnContext).getApnSetting();
@@ -526,7 +542,7 @@
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
         assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+        assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
     }
 
@@ -902,4 +918,40 @@
     public void testStartNattKeepaliveFailCondensed() throws Exception {
         checkStartNattKeepaliveFail(true);
     }
+
+    @Test
+    @SmallTest
+    public void testIsUnmeteredUseOnly() throws Exception {
+        Field field = DataConnection.class.getDeclaredField("mTransportType");
+        field.setAccessible(true);
+        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+        assertFalse(isUnmeteredUseOnly());
+
+        field = DataConnection.class.getDeclaredField("mTransportType");
+        field.setAccessible(true);
+        field.setInt(mDc, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        doReturn(false).when(mDataEnabledSettings).isDataEnabled();
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(ApnSetting.TYPE_MMS).when(mApnContext).getApnTypeBitmask();
+
+        mContextFixture.getCarrierConfigBundle().putStringArray(
+                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[] { "default" });
+
+        assertTrue(isUnmeteredUseOnly());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetDisallowedApnTypes() throws Exception {
+        mContextFixture.getCarrierConfigBundle().putStringArray(
+                CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+                new String[] { "mms", "supl", "fota" });
+        testConnectEvent();
+
+        assertEquals(ApnSetting.TYPE_MMS | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA,
+                getDisallowedApnTypes());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index 4c80685..290c535 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -17,17 +17,21 @@
 package com.android.internal.telephony.emergency;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 
 import android.os.AsyncResult;
 import android.os.HandlerThread;
 import android.telephony.emergency.EmergencyNumber;
 
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -38,8 +42,16 @@
  */
 public class EmergencyNumberTrackerTest extends TelephonyTest {
 
+    @Mock
+    private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+
+    // mEmergencyNumberTrackerMock for mPhone
     private EmergencyNumberTracker mEmergencyNumberTrackerMock;
+    // mEmergencyNumberTrackerMock2 for mPhone2
+    private EmergencyNumberTracker mEmergencyNumberTrackerMock2;
+
     private List<EmergencyNumber> mEmergencyNumberListTestSample = new ArrayList<>();
+    private EmergencyNumber mUsEmergencyNumber;
     private String[] mEmergencyNumberPrefixTestSample = {"123", "456"};
     private static final long TIMEOUT_MS = 500;
 
@@ -50,6 +62,8 @@
         @Override
         public void onLooperPrepared() {
             mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
+            mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands);
+            doReturn(mEmergencyNumberTrackerMock2).when(mPhone2).getEmergencyNumberTracker();
             mEmergencyNumberTrackerMock.DBG = true;
             setReady(true);
         }
@@ -62,6 +76,11 @@
         logd("EmergencyNumberTrackerTest +Setup!");
         super.setUp("EmergencyNumberTrackerTest");
         doReturn(mContext).when(mPhone).getContext();
+        doReturn(0).when(mPhone).getPhoneId();
+
+        doReturn(mContext).when(mPhone2).getContext();
+        doReturn(1).when(mPhone2).getPhoneId();
+
         initializeEmergencyNumberListTestSamples();
         mHandlerThread = new EmergencyNumberTrackerTestHandler("EmergencyNumberTrackerTestHandler");
         mHandlerThread.start();
@@ -71,6 +90,9 @@
 
     @After
     public void tearDown() throws Exception {
+        // Set back to single sim mode
+        setSinglePhone();
+
         mHandlerThread.quit();
         mHandlerThread.join();
         super.tearDown();
@@ -82,6 +104,12 @@
                 new ArrayList<String>(),
                 EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
                 EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+        mUsEmergencyNumber = new EmergencyNumber("911", "us", "",
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+                | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE, new ArrayList<String>(),
+            EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+            EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
         mEmergencyNumberListTestSample.add(emergencyNumberForTest);
     }
 
@@ -93,11 +121,26 @@
         waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
     }
 
-    private void sendEmergencyNumberPrefix() {
-        mEmergencyNumberTrackerMock.obtainMessage(
+    private void cacheEmergencyNumberListFromDatabaseByCountry(String countryIso) {
+        mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange(countryIso);
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+    }
+
+    private void sendEmergencyNumberPrefix(EmergencyNumberTracker emergencyNumberTrackerMock) {
+        emergencyNumberTrackerMock.obtainMessage(
         	4 /* EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX */,
                 mEmergencyNumberPrefixTestSample).sendToTarget();
-        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+        waitForHandlerAction(emergencyNumberTrackerMock, TIMEOUT_MS);
+    }
+
+    private void setDsdsPhones() throws Exception {
+        mPhones = new Phone[] {mPhone, mPhone2};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+    }
+
+    private void setSinglePhone() throws Exception {
+        mPhones = new Phone[] {mPhone};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
     }
 
     @Test
@@ -108,9 +151,66 @@
     }
 
     @Test
+    public void testUpdateEmergencyCountryIso() throws Exception {
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
+        mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange("us");
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+
+        assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("us"));
+    }
+
+    @Test
+    public void testUpdateEmergencyCountryIsoMultiSim() throws Exception {
+        setDsdsPhones();
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock2);
+        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("jp");
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+        waitForHandlerAction(mEmergencyNumberTrackerMock2, TIMEOUT_MS);
+
+        assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("jp"));
+        assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("jp"));
+    }
+
+    @Test
+    public void testUpdateEmergencyCountryIsoFromAnotherSimOrNot() throws Exception {
+        setDsdsPhones();
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock2);
+
+        // First, both slots have empty country iso, trigger a country change to "jp".
+        // We should expect both sims have "jp" country iso.
+        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("jp");
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+        waitForHandlerAction(mEmergencyNumberTrackerMock2, TIMEOUT_MS);
+        assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("jp"));
+        assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("jp"));
+
+        // Second, both slots now have "jp" country iso, trigger a country change to "us".
+        // We should expect both sims have "us" country iso.
+        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+        waitForHandlerAction(mEmergencyNumberTrackerMock2, TIMEOUT_MS);
+        assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("us"));
+        assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("us"));
+
+        // Third, both slots now have "us" country iso, manually configure
+        // "mIsCountrySetByAnotherSub" flag in "mPhone2" as false, and trigger a country
+        // change to "ca". We should expect the current phone to change the country iso
+        // to "ca", and should expect the other phone *not* to change their country iso
+        // to "ca".
+        mEmergencyNumberTrackerMock2.mIsCountrySetByAnotherSub = false;
+        mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("ca");
+        waitForHandlerAction(mEmergencyNumberTrackerMock, TIMEOUT_MS);
+        waitForHandlerAction(mEmergencyNumberTrackerMock2, TIMEOUT_MS);
+        assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("ca"));
+        assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("us"));
+    }
+
+    @Test
     public void testEmergencyNumberListPrefix() throws Exception {
         sendEmergencyNumberListFromRadio();
-        sendEmergencyNumberPrefix();
+        sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
         List<EmergencyNumber> resultToVerify = mEmergencyNumberListTestSample;
         resultToVerify.add(new EmergencyNumber("123119", "jp", "30",
                 EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index b7c450c..4062b31 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -58,7 +59,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -96,6 +96,7 @@
     private ImsResolver mTestImsResolver;
     private BroadcastReceiver mTestPackageBroadcastReceiver;
     private BroadcastReceiver mTestCarrierConfigReceiver;
+    private BroadcastReceiver mTestBootCompleteReceiver;
     private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
     private PersistableBundle[] mCarrierConfigs;
 
@@ -793,6 +794,50 @@
         verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
     }
 
+    /**
+     * Inform the ImsResolver that BOOT_COMPLETE has happened. A non-FBE enabled ImsService is now
+     * available to be bound.
+     */
+    @Test
+    @SmallTest
+    public void testBootCompleteNonFbeEnabledCarrierImsService() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        setupPackageQuery(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        // Bind with device ImsService
+        startBind();
+
+        // Boot complete happens and the Carrier ImsService is now available.
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the voice feature.
+        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+        // Boot complete has happened and the carrier ImsService is now available.
+        mTestBootCompleteReceiver.onReceive(null, new Intent(Intent.ACTION_BOOT_COMPLETED));
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+        // Verify that all features that have been defined for the carrier override are bound
+        verify(carrierController).bind(carrierFeatures);
+        // device features change
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+                convertToHashSet(deviceFeatures, 1);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        deviceFeatureSet.removeAll(carrierFeatures);
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
     private void setupResolver(int numSlots) {
         when(mMockContext.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE))).thenReturn(
                 mMockCarrierConfigManager);
@@ -811,13 +856,14 @@
 
         ArgumentCaptor<BroadcastReceiver> packageBroadcastCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        ArgumentCaptor<BroadcastReceiver> carrierConfigCaptor =
+        ArgumentCaptor<BroadcastReceiver> receiversCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mMockContext).registerReceiverAsUser(packageBroadcastCaptor.capture(), any(),
                 any(), any(), any());
-        verify(mMockContext).registerReceiver(carrierConfigCaptor.capture(), any());
-        mTestCarrierConfigReceiver = carrierConfigCaptor.getValue();
         mTestPackageBroadcastReceiver = packageBroadcastCaptor.getValue();
+        verify(mMockContext, times(2)).registerReceiver(receiversCaptor.capture(), any());
+        mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(0);
+        mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(1);
         mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
         when(mMockQueryManagerFactory.create(any(Context.class),
                 any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
@@ -861,7 +907,7 @@
     }
 
     private void startBind() {
-        mTestImsResolver.initPopulateCacheAndStartBind();
+        mTestImsResolver.initialize();
         ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
                 ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
         verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
@@ -876,7 +922,7 @@
         // ensure that startQuery was called
         when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
                 .thenReturn(true);
-        verify(mMockQueryManager, Mockito.times(times)).startQuery(eq(name), any(String.class));
+        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
         mDynamicQueryListener.onComplete(name, features);
         // wait for handling of onComplete
         waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);