Merge "API review: change to use getter to access a field"
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 387904e..104b5c3 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -60,6 +60,9 @@
     private static final int GET_PS_REGISTRATION_STATE_DONE = 2;
     private static final int NETWORK_REGISTRATION_STATE_CHANGED = 3;
 
+    // From 24.008 6.1.3.0 and 10.5.6.2 the maximum number of PDP Contexts is 16.
+    private static final int MAX_DATA_CALLS = 16;
+
     private class CellularNetworkServiceProvider extends NetworkServiceProvider {
 
         private final Map<Message, NetworkServiceCallback> mCallbackMap = new HashMap<>();
@@ -216,7 +219,13 @@
             int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
             int domain = NetworkRegistrationInfo.DOMAIN_CS;
 
-            if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
+            // 1.5 at the top so that we can do an "early exit" from the method
+            if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
+                return getNetworkRegistrationInfo(
+                        NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                        (android.hardware.radio.V1_5.RegStateResult) result);
+            } else if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
                 android.hardware.radio.V1_0.VoiceRegStateResult voiceRegState =
                         (android.hardware.radio.V1_0.VoiceRegStateResult) result;
                 int regState = getRegStateFromHalRegState(voiceRegState.regState);
@@ -285,7 +294,13 @@
                     new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
                             LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
 
-            if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
+            // 1.5 at the top so that we can do an "early exit" from the method
+            if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
+                return getNetworkRegistrationInfo(
+                        NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                        (android.hardware.radio.V1_5.RegStateResult) result);
+            } else if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
                 android.hardware.radio.V1_0.DataRegStateResult dataRegState =
                         (android.hardware.radio.V1_0.DataRegStateResult) result;
                 regState = getRegStateFromHalRegState(dataRegState.regState);
@@ -341,8 +356,14 @@
             List<Integer> availableServices = getAvailableServices(
                     regState, domain, emergencyOnly);
 
+            // In earlier versions of the HAL, LTE_CA was allowed to indicate that the device
+            // is on CA; however, that has been superseded by the PHYSICAL_CHANNEL_CONFIG signal.
+            // Because some vendors provide both NETWORK_TYPE_LTE_CA *and* PHYSICAL_CHANNEL_CONFIG,
+            // this tweak is left for compatibility; however, the network type is no longer allowed
+            // to be used to declare that carrier aggregation is in effect, because the other
+            // signal provides a much richer information set, and we want to mitigate confusion in
+            // how CA information is being provided.
             if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
-                isUsingCarrierAggregation = true;
                 networkType = TelephonyManager.NETWORK_TYPE_LTE;
             }
 
@@ -352,6 +373,87 @@
                     lteVopsSupportInfo, isUsingCarrierAggregation);
         }
 
+        private @NonNull NetworkRegistrationInfo getNetworkRegistrationInfo(
+                int domain, int transportType,
+                android.hardware.radio.V1_5.RegStateResult regResult) {
+
+            // Perform common conversions that aren't domain specific
+            final int regState = getRegStateFromHalRegState(regResult.regState);
+            final boolean isEmergencyOnly = isEmergencyOnly(regResult.regState);
+            final List<Integer> availableServices = getAvailableServices(
+                    regState, domain, isEmergencyOnly);
+            final int rejectCause = regResult.reasonForDenial;
+            final CellIdentity cellIdentity = CellIdentity.create(regResult.cellIdentity);
+            final String rplmn = regResult.registeredPlmn;
+            final int reasonForDenial = regResult.reasonForDenial;
+
+            // Network Type fixup for carrier aggregation
+            int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
+            boolean isUsingCarrierAggregation = false;
+            if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+                isUsingCarrierAggregation = true;
+                networkType = TelephonyManager.NETWORK_TYPE_LTE;
+            }
+
+            // Conditional parameters for specific RANs
+            boolean cssSupported = false;
+            int roamingIndicator = 0;
+            int systemIsInPrl = 0;
+            int defaultRoamingIndicator = 0;
+            boolean isEndcAvailable = false;
+            boolean isNrAvailable = false;
+            boolean isDcNrRestricted = false;
+            LteVopsSupportInfo vopsInfo = new LteVopsSupportInfo(
+                    LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                    LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+
+            switch (regResult.accessTechnologySpecificInfo.getDiscriminator()) {
+                case android.hardware.radio.V1_5.RegStateResult
+                        .AccessTechnologySpecificInfo.hidl_discriminator.cdmaInfo:
+                    android.hardware.radio.V1_5.RegStateResult
+                            .AccessTechnologySpecificInfo.Cdma2000RegistrationInfo cdmaInfo =
+                                    regResult.accessTechnologySpecificInfo.cdmaInfo();
+                    cssSupported = cdmaInfo.cssSupported;
+                    roamingIndicator = cdmaInfo.roamingIndicator;
+                    systemIsInPrl = cdmaInfo.systemIsInPrl;
+                    defaultRoamingIndicator = cdmaInfo.defaultRoamingIndicator;
+                    break;
+                case android.hardware.radio.V1_5.RegStateResult
+                        .AccessTechnologySpecificInfo.hidl_discriminator.eutranInfo:
+                    android.hardware.radio.V1_5.RegStateResult
+                            .AccessTechnologySpecificInfo.EutranRegistrationInfo eutranInfo =
+                                    regResult.accessTechnologySpecificInfo.eutranInfo();
+
+                    isEndcAvailable = eutranInfo.nrIndicators.isDcNrRestricted;
+                    isNrAvailable = eutranInfo.nrIndicators.isNrAvailable;
+                    isDcNrRestricted = eutranInfo.nrIndicators.isEndcAvailable;
+                    vopsInfo = convertHalLteVopsSupportInfo(
+                            eutranInfo.lteVopsInfo.isVopsSupported,
+                            eutranInfo.lteVopsInfo.isEmcBearerSupported);
+                    break;
+                default:
+                    log("No access tech specific info passes for RegStateResult");
+                    break;
+            }
+
+            // build the result based on the domain for the request
+            switch(domain) {
+                case NetworkRegistrationInfo.DOMAIN_CS:
+                    return new NetworkRegistrationInfo(domain, transportType, regState,
+                            networkType, reasonForDenial, isEmergencyOnly, availableServices,
+                            cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
+                            defaultRoamingIndicator);
+                default:
+                    loge("Unknown domain passed to CellularNetworkService= " + domain);
+                    // fall through
+                case NetworkRegistrationInfo.DOMAIN_PS:
+                    return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
+                            reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
+                            rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
+                            vopsInfo, isUsingCarrierAggregation);
+            }
+        }
+
         private LteVopsSupportInfo convertHalLteVopsSupportInfo(
                 boolean vopsSupport, boolean emcBearerSupport) {
             int vops = LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED;
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index c6ea624..56646de 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -208,12 +208,6 @@
     }
 
     @Override
-    public void notifyOemHookRawEventForSubscriber(Phone sender, byte[] rawData) {
-        mTelephonyRegistryMgr.notifyOemHookRawEventForSubscriber(sender.getSubId(),
-            sender.getPhoneId(), rawData);
-    }
-
-    @Override
     public void notifyDisplayInfoChanged(Phone sender, DisplayInfo displayInfo) {
         mTelephonyRegistryMgr.notifyDisplayInfoChanged(sender.getPhoneId(), sender.getSubId(),
                 displayInfo);
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 7766b16..0bad35a 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -68,6 +68,7 @@
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsConfig;
+import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -715,13 +716,7 @@
                 break;
 
             case EVENT_UNSOL_OEM_HOOK_RAW:
-                ar = (AsyncResult)msg.obj;
-                if (ar.exception == null) {
-                    byte[] data = (byte[])ar.result;
-                    mNotifier.notifyOemHookRawEventForSubscriber(this, data);
-                } else {
-                    Rlog.e(LOG_TAG, "OEM hook raw exception: " + ar.exception);
-                }
+                // deprecated, ignore
                 break;
 
             case EVENT_CONFIG_LCE:
@@ -3686,7 +3681,7 @@
      * @return true if the IMS capability for the registration technology specified is available,
      * false otherwise.
      */
-    public boolean isImsCapabilityAvailable(int capability, int regTech) {
+    public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
         Phone imsPhone = mImsPhone;
         boolean isAvailable = false;
         if (imsPhone != null) {
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index d5e5be1..c595a6c 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -87,9 +87,6 @@
     /** Send a notification that the users mobile data setting has changed */
     void notifyUserMobileDataStateChanged(Phone sender, boolean state);
 
-    /** Send a notification with an OEM hook payload */
-    void notifyOemHookRawEventForSubscriber(Phone sender, byte[] rawData);
-
     /** Send a notification that the display info has changed */
     void notifyDisplayInfoChanged(Phone sender, DisplayInfo displayInfo);
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 0ed5a6a..4e7899a 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1347,10 +1347,20 @@
 
             if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-            try {
-                radioProxy.getVoiceRegistrationState(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState", e);
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                final android.hardware.radio.V1_5.IRadio radioProxy15 =
+                        (android.hardware.radio.V1_5.IRadio) radioProxy;
+                try {
+                    radioProxy15.getVoiceRegistrationState_1_5(rr.mSerial);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState_1_5", e);
+                }
+            } else {
+                try {
+                    radioProxy.getVoiceRegistrationState(rr.mSerial);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState", e);
+                }
             }
         }
     }
@@ -1364,10 +1374,21 @@
 
             if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-            try {
-                radioProxy.getDataRegistrationState(rr.mSerial);
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(rr, "getDataRegistrationState", e);
+
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                final android.hardware.radio.V1_5.IRadio radioProxy15 =
+                        (android.hardware.radio.V1_5.IRadio) radioProxy;
+                try {
+                    radioProxy15.getDataRegistrationState_1_5(rr.mSerial);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "getDataRegistrationState_1_5", e);
+                }
+            } else {
+                try {
+                    radioProxy.getDataRegistrationState(rr.mSerial);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "getDataRegistrationState", e);
+                }
             }
         }
     }
@@ -6542,6 +6563,38 @@
         return response;
     }
 
+    private static LinkAddress createLinkAddressFromString(String addressString) {
+        return createLinkAddressFromString(addressString, 0, LinkAddress.LIFETIME_UNKNOWN,
+                LinkAddress.LIFETIME_UNKNOWN);
+    }
+
+    private static LinkAddress createLinkAddressFromString(String addressString, int properties,
+            long deprecationTime, long expirationTime) {
+        addressString = addressString.trim();
+        InetAddress address = null;
+        int prefixLength = -1;
+        try {
+            String[] pieces = addressString.split("/", 2);
+            address = InetAddresses.parseNumericAddress(pieces[0]);
+            if (pieces.length == 1) {
+                prefixLength = (address instanceof Inet4Address) ? 32 : 128;
+            } else if (pieces.length == 2) {
+                prefixLength = Integer.parseInt(pieces[1]);
+            }
+        } catch (NullPointerException e) {            // Null string.
+        } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
+        } catch (NumberFormatException e) {           // Non-numeric prefix.
+        } catch (IllegalArgumentException e) {        // Invalid IP address.
+        }
+
+        if (address == null || prefixLength == -1) {
+            throw new IllegalArgumentException("Invalid link address " + addressString);
+        }
+
+        return new LinkAddress(address, prefixLength, properties, 0,
+                deprecationTime, expirationTime);
+    }
+
     /**
      * Convert SetupDataCallResult defined in 1.0, 1.4, or 1.5 types.hal into DataCallResponse
      * @param dcResult setup data call result
@@ -6611,9 +6664,9 @@
             active = result.active;
             protocolType = result.type;
             ifname = result.ifname;
-            laList = result.addresses.stream().map(a -> new LinkAddress(
-                    InetAddresses.parseNumericAddress(a.address), 0, a.properties, 0,
-                    a.deprecationTime, a.expirationTime)).collect(Collectors.toList());
+            laList = result.addresses.stream().map(la -> createLinkAddressFromString(
+                    la.address, la.properties, la.deprecationTime, la.expirationTime))
+                    .collect(Collectors.toList());
 
             dnses = result.dnses.stream().toArray(String[]::new);
             gateways = result.gateways.stream().toArray(String[]::new);
@@ -6631,24 +6684,7 @@
             // Process address
             if (addresses != null) {
                 for (String address : addresses) {
-                    address = address.trim();
-                    if (address.isEmpty()) continue;
-
-                    try {
-                        LinkAddress la;
-                        // Check if the address contains prefix length. If yes, LinkAddress
-                        // can parse that.
-                        if (address.split("/").length == 2) {
-                            la = new LinkAddress(address);
-                        } else {
-                            InetAddress ia = InetAddresses.parseNumericAddress(address);
-                            la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128);
-                        }
-
-                        laList.add(la);
-                    } catch (IllegalArgumentException e) {
-                        Rlog.e(RILJ_LOG_TAG, "Unknown address: " + address, e);
-                    }
+                    laList.add(createLinkAddressFromString(address));
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index dc3baf3..4469b8b 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -84,7 +84,7 @@
 import android.hardware.radio.V1_0.StkCcUnsolSsResult;
 import android.hardware.radio.V1_0.SuppSvcNotification;
 import android.hardware.radio.V1_2.CellConnectionStatus;
-import android.hardware.radio.V1_4.IRadioIndication;
+import android.hardware.radio.V1_5.IRadioIndication;
 import android.os.AsyncResult;
 import android.sysprop.TelephonyProperties;
 import android.telephony.Annotation.RadioPowerState;
@@ -1022,7 +1022,7 @@
      */
     public void barringInfoChanged(int indicationType,
             android.hardware.radio.V1_5.CellIdentity cellIdentity,
-            List<android.hardware.radio.V1_5.BarringInfo> barringInfos) {
+            ArrayList<android.hardware.radio.V1_5.BarringInfo> barringInfos) {
         mRil.processIndication(indicationType);
 
         CellIdentity ci = CellIdentity.create(cellIdentity);
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 7165a40..8e2033e 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -192,6 +192,18 @@
         responseInts(responseInfo, retriesRemaining);
     }
 
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param persoType SIM Personalisation type
+     * @param remainingRetries postiive values indicates number of retries remaining,
+     * must be equal to -1 if number of retries is infinite.
+     */
+    public void supplySimDepersonalizationResponse(RadioResponseInfo info,
+            int persoType, int remainingRetries) {
+        //short-term stub method
+    }
+
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      * @param calls Current call list
@@ -339,6 +351,23 @@
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
+     *        in 1.5/types.hal
+     */
+    public void getVoiceRegistrationStateResponse_1_5(RadioResponseInfo responseInfo,
+            android.hardware.radio.V1_5.RegStateResult voiceRegResponse) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, voiceRegResponse);
+            }
+            mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
      * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
      *        types.hal
      */
@@ -388,6 +417,22 @@
         }
     }
 
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
+     *        1.5/types.hal
+     */
+    public void getDataRegistrationStateResponse_1_5(RadioResponseInfo responseInfo,
+            android.hardware.radio.V1_5.RegStateResult dataRegResponse) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dataRegResponse);
+            }
+            mRil.processResponseDone(rr, responseInfo, dataRegResponse);
+        }
+    }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index eae668c..5d41e73 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -213,6 +213,9 @@
         // clear SLOT_INDEX for all subs
         clearSlotIndexForSubInfoRecords();
 
+        // Cache Setting values
+        cacheSettingValues();
+
         if (DBG) logdl("[SubscriptionController] init by Context");
     }
 
@@ -248,6 +251,26 @@
         mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, null, null);
     }
 
+    /**
+     * Cache the Settings values by reading these values from Setting from disk to prevent disk I/O
+     * access during the API calling. This is based on an assumption that the Settings system will
+     * itself cache this value after the first read and thus only the first read after boot will
+     * access the disk.
+     */
+    private void cacheSettingValues() {
+        Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
     @UnsupportedAppUsage
     protected void enforceModifyPhoneState(String message) {
         mContext.enforceCallingOrSelfPermission(
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 097caa4..dfb9326 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -380,10 +380,8 @@
 
         // Log all proactive commands.
         if (isProactiveCmd) {
-            if (mUiccController != null) {
-                mUiccController.addCardLog("ProactiveCommand mSlotId=" + mSlotId +
-                        " cmdParams=" + cmdParams);
-            }
+            UiccController.addLocalLog("CatService[" + mSlotId + "]: ProactiveCommand " +
+                    " cmdParams=" + cmdParams);
         }
 
         CharSequence message;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 41fd67d..3eb22f7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -1656,37 +1656,6 @@
                                 + " drs=" + mDataRegState
                                 + " mRilRat=" + mRilRat);
                     }
-                    updateNetworkInfoSuspendState();
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                        mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
-                        mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
-                    }
-                    break;
-                case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED:
-                    boolean isUnmetered = (boolean) msg.obj;
-                    if (isUnmetered == mUnmeteredOverride) {
-                        break;
-                    }
-                    mUnmeteredOverride = isUnmetered;
-                    // fallthrough
-                case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_DATA_CONNECTION_ROAM_ON:
-                case EVENT_DATA_CONNECTION_ROAM_OFF:
-                case EVENT_DATA_CONNECTION_OVERRIDE_CHANGED:
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
-                                DataConnection.this);
-                        mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
-                    }
-                    break;
-                case EVENT_KEEPALIVE_START_REQUEST:
-                case EVENT_KEEPALIVE_STOP_REQUEST:
-                    if (mNetworkAgent != null) {
-                        mNetworkAgent.sendSocketKeepaliveEvent(
-                                msg.arg1, SocketKeepalive.ERROR_INVALID_NETWORK);
-                    }
                     break;
                 default:
                     if (DBG) {
@@ -2261,9 +2230,38 @@
                     retVal = HANDLED;
                     break;
                 }
+                case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result;
+                    mDataRegState = drsRatPair.first;
+                    updateTcpBufferSizes(drsRatPair.second);
+                    mRilRat = drsRatPair.second;
+                    if (DBG) {
+                        log("DcActiveState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
+                                + " drs=" + mDataRegState
+                                + " mRilRat=" + mRilRat);
+                    }
+                    updateNetworkInfoSuspendState();
+                    if (mNetworkAgent != null) {
+                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
+                                DataConnection.this);
+                        mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
+                        mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
+                    }
+                    retVal = HANDLED;
+                    break;
+                }
+                case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED:
+                    boolean isUnmetered = (boolean) msg.obj;
+                    if (isUnmetered == mUnmeteredOverride) {
+                        retVal = HANDLED;
+                        break;
+                    }
+                    mUnmeteredOverride = isUnmetered;
+                    // fallthrough
+                case EVENT_NR_FREQUENCY_CHANGED:
                 case EVENT_DATA_CONNECTION_ROAM_ON:
                 case EVENT_DATA_CONNECTION_ROAM_OFF:
-                case EVENT_DATA_CONNECTION_METEREDNESS_CHANGED:
                 case EVENT_DATA_CONNECTION_OVERRIDE_CHANGED: {
                     if (mNetworkAgent != null) {
                         mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 66f5ceb..dda5d0d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -135,7 +135,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.IntStream;
+import java.util.stream.Collectors;
 
 /**
  * {@hide}
@@ -4143,20 +4143,23 @@
         }
 
         boolean isGeneralUnmetered = true;
+        Set<Integer> allNetworkTypes = Arrays.stream(TelephonyManager.getAllNetworkTypes())
+                .boxed().collect(Collectors.toSet());
         for (SubscriptionPlan plan : mSubscriptionPlans) {
-            // check plan is general or specific
-            if (plan.getNetworkTypes() == null) {
+            // check plan is general (applies to all network types) or specific
+            if (Arrays.stream(plan.getNetworkTypes()).boxed().collect(Collectors.toSet())
+                    .containsAll(allNetworkTypes)) {
                 if (!isPlanUnmetered(plan)) {
                     // metered takes precedence over unmetered for safety
                     isGeneralUnmetered = false;
                 }
             } else {
                 // check plan applies to given network type
-                if (IntStream.of(plan.getNetworkTypes()).anyMatch(n -> n == networkType)) {
-                    // ensure network type unknown returns general value
-                    if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
-                        // there is only 1 specific plan per network type, so return value if found
-                        return isPlanUnmetered(plan);
+                if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+                    for (int planNetworkType : plan.getNetworkTypes()) {
+                        if (planNetworkType == networkType) {
+                            return isPlanUnmetered(plan);
+                        }
                     }
                 }
             }
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 5ff2536..b8bab7d 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -34,6 +34,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -405,6 +406,8 @@
     // Persistent Logging
     private final LocalLog mEventLog = new LocalLog(50);
 
+    private boolean mBootCompletedHandlerRan = false;
+
     // Synchronize all events on a handler to ensure that the cache includes the most recent
     // version of the installed ImsServices.
     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
@@ -420,9 +423,12 @@
                 break;
             }
             case HANDLER_BOOT_COMPLETE: {
-                mEventLog.log("handling BOOT_COMPLETE");
-                // Re-evaluate bound services for all slots after requerying packagemanager
-                maybeAddedImsService(null /*packageName*/);
+                if (!mBootCompletedHandlerRan) {
+                    mBootCompletedHandlerRan = true;
+                    mEventLog.log("handling BOOT_COMPLETE");
+                    // Re-evaluate bound services for all slots after requerying packagemanager
+                    maybeAddedImsService(null /*packageName*/);
+                }
                 break;
             }
             case HANDLER_CONFIG_CHANGED: {
@@ -539,8 +545,17 @@
         mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
         mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
-        mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
-                Intent.ACTION_BOOT_COMPLETED));
+
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        if (userManager.isUserUnlocked()) {
+            mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
+        } else {
+            mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
+                    Intent.ACTION_BOOT_COMPLETED));
+            if (userManager.isUserUnlocked()) {
+                mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
+            }
+        }
     }
 
     @VisibleForTesting
@@ -1179,6 +1194,8 @@
 
     // Called from handler ONLY.
     private void carrierConfigChanged(int slotId) {
+        updateBoundDeviceServices();
+
         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++) {
@@ -1188,11 +1205,28 @@
         updateBoundServices(slotId, getImsPackageOverrideConfig(slotId));
     }
 
+    private void updateBoundDeviceServices() {
+        Log.d(TAG, "updateBoundDeviceServices: called");
+        ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>();
+        for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
+            String packageName = getDeviceConfiguration(f);
+            ImsServiceInfo serviceInfo = getImsServiceInfoFromCache(packageName);
+            if (serviceInfo != null && !serviceInfo.featureFromMetadata
+                    && !featureDynamicImsPackages.containsKey(packageName)) {
+                featureDynamicImsPackages.put(packageName, serviceInfo);
+
+                Log.d(TAG, "updateBoundDeviceServices: Schedule query for package=" + packageName);
+                scheduleQueryForFeatures(featureDynamicImsPackages.get(packageName));
+            }
+        }
+    }
+
     private void updateBoundServices(int slotId, Map<Integer, String> featureMap) {
         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlots) {
             return;
         }
         boolean hasConfigChanged = false;
+        boolean didQuerySchedule = false;
         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
             String overridePackageName = getOverridePackageName(slotId, f);
             String oldPackageName = getCarrierConfiguredPackageName(slotId, f);
@@ -1207,20 +1241,28 @@
                         ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid"), slotId));
                 newPackageName = overridePackageName;
             }
-            mEventLog.log("updateBoundServices - carrier package changed: "
-                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId);
+
             setCarrierConfiguredPackageName(newPackageName, slotId, f);
             // Carrier config may have not changed, but we still want to kick off a recalculation
             // in case there has been a change to the supported device features.
             ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+            mEventLog.log("updateBoundServices - carrier package changed: "
+                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+                    + ", hasConfigChanged=" + hasConfigChanged);
             if (info == null || info.featureFromMetadata) {
                 hasConfigChanged = true;
             } else {
                 // Config will change when this query completes
                 scheduleQueryForFeatures(info);
+                didQuerySchedule = true;
             }
         }
         if (hasConfigChanged) calculateFeatureConfigurationChange();
+
+        if (hasConfigChanged && didQuerySchedule) {
+            mEventLog.log("[warning] updateBoundServices - both hasConfigChange and query "
+                    + "scheduled on slot " + slotId);
+        }
     }
 
     private @NonNull Map<Integer, String> getImsPackageOverrideConfig(int slotId) {
@@ -1384,6 +1426,10 @@
         service.replaceFeatures(features);
         // Wait until all queries have completed before changing the configuration to reduce churn.
         if (!mFeatureQueryManager.isQueryInProgress()) {
+            if (mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)) {
+                mEventLog.log("[warning] dynamicQueryComplete - HANDLER_DYNAMIC_FEATURE_CHANGE "
+                        + "pending with calculateFeatureConfigurationChange()");
+            }
             calculateFeatureConfigurationChange();
         }
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index b9a8412..0597db6 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1813,7 +1813,7 @@
     }
 
     @Override
-    public boolean isImsCapabilityAvailable(int capability, int regTech) {
+    public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
         return mCT.isImsCapabilityAvailable(capability, regTech);
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 629e10e..37eaa35 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -4185,20 +4185,25 @@
     }
 
     /**
+     * Contacts the ImsService directly for capability information.  May be slow.
      * @return true if the IMS capability for the specified registration technology is currently
      * available.
      */
-    public boolean isImsCapabilityAvailable(int capability, int regTech) {
-        return (getImsRegistrationTech() == regTech) && mMmTelCapabilities.isCapable(capability);
+    public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
+        if (mImsManager != null) {
+            return mImsManager.queryMmTelCapabilityStatus(capability, regTech);
+        } else {
+            return false;
+        }
     }
 
     public boolean isVolteEnabled() {
-        return isImsCapabilityAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+        return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
     }
 
     public boolean isVowifiEnabled() {
-        return isImsCapabilityAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+        return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
     }
 
@@ -4207,6 +4212,10 @@
         return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
     }
 
+    private boolean isImsCapabilityInCacheAvailable(int capability, int regTech) {
+        return (getImsRegistrationTech() == regTech) && mMmTelCapabilities.isCapable(capability);
+    }
+
     @Override
     public PhoneConstants.State getState() {
         return mState;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index c746c97..8f009e7 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -50,6 +50,7 @@
 import com.android.internal.telephony.RadioConfig;
 import com.android.internal.telephony.SubscriptionInfoUpdater;
 import com.android.internal.telephony.uicc.euicc.EuiccCard;
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -125,6 +126,7 @@
     private static final int EVENT_SIM_REFRESH = 8;
     private static final int EVENT_EID_READY = 9;
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 10;
+    // NOTE: any new EVENT_* values must be added to eventToString.
 
     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
     @UnsupportedAppUsage
@@ -189,7 +191,7 @@
     private RadioConfig mRadioConfig;
 
     // LocalLog buffer to hold important SIM related events for debugging
-    static LocalLog sLocalLog = new LocalLog(100);
+    private static LocalLog sLocalLog = new LocalLog(TelephonyUtils.IS_DEBUGGABLE ? 250 : 100);
 
     /**
      * API to make UiccController singleton if not already created.
@@ -208,14 +210,11 @@
         if (DBG) log("Creating UiccController");
         mContext = c;
         mCis = PhoneFactory.getCommandsInterfaces();
-        if (DBG) {
-            String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
-                    com.android.internal.R.integer.config_num_physical_slots);
-            log(logStr);
-            sLocalLog.log(logStr);
-        }
         int numPhysicalSlots = c.getResources().getInteger(
                 com.android.internal.R.integer.config_num_physical_slots);
+        if (DBG) {
+            logWithLocalLog("config_num_physical_slots = " + numPhysicalSlots);
+        }
         // Minimum number of physical slot count should be equals to or greater than phone count,
         // if it is less than phone count use phone count as physical slot count.
         if (numPhysicalSlots < mCis.length) {
@@ -362,6 +361,7 @@
 
     /** Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. */
     public void switchSlots(int[] physicalSlots, Message response) {
+        logWithLocalLog("switchSlots: " + Arrays.toString(physicalSlots));
         mRadioConfig.setSimSlotsMapping(physicalSlots, response);
     }
 
@@ -467,14 +467,15 @@
     public void handleMessage (Message msg) {
         synchronized (mLock) {
             Integer phoneId = getCiIndex(msg);
+            String eventName = eventToString(msg.what);
 
             if (phoneId < 0 || phoneId >= mCis.length) {
                 Rlog.e(LOG_TAG, "Invalid phoneId : " + phoneId + " received with event "
-                        + msg.what);
+                        + eventName);
                 return;
             }
 
-            sLocalLog.log("handleMessage: Received " + msg.what + " for phoneId " + phoneId);
+            logWithLocalLog("handleMessage: Received " + eventName + " for phoneId " + phoneId);
 
             AsyncResult ar = (AsyncResult)msg.obj;
             switch (msg.what) {
@@ -541,14 +542,18 @@
     private void onMultiSimConfigChanged() {
         int prevActiveModemCount = mCis.length;
         mCis = PhoneFactory.getCommandsInterfaces();
+        int newActiveModemCount = mCis.length;
+
+        logWithLocalLog("onMultiSimConfigChanged: prevActiveModemCount " + prevActiveModemCount
+                + ", newActiveModemCount " + newActiveModemCount);
 
         // Resize array.
-        mPhoneIdToSlotId = copyOf(mPhoneIdToSlotId, mCis.length);
+        mPhoneIdToSlotId = copyOf(mPhoneIdToSlotId, newActiveModemCount);
 
         // Register for new active modem for ss -> ds switch.
         // For ds -> ss switch, there's no need to unregister as the mCis should unregister
         // everything itself.
-        for (int i = prevActiveModemCount; i < mCis.length; i++) {
+        for (int i = prevActiveModemCount; i < newActiveModemCount; i++) {
             mPhoneIdToSlotId[i] = INVALID_SLOT_ID;
             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
 
@@ -590,6 +595,22 @@
         return index;
     }
 
+    private static String eventToString(int event) {
+        switch (event) {
+            case EVENT_ICC_STATUS_CHANGED: return "ICC_STATUS_CHANGED";
+            case EVENT_SLOT_STATUS_CHANGED: return "SLOT_STATUS_CHANGED";
+            case EVENT_GET_ICC_STATUS_DONE: return "GET_ICC_STATUS_DONE";
+            case EVENT_GET_SLOT_STATUS_DONE: return "GET_SLOT_STATUS_DONE";
+            case EVENT_RADIO_ON: return "RADIO_ON";
+            case EVENT_RADIO_AVAILABLE: return "RADIO_AVAILABLE";
+            case EVENT_RADIO_UNAVAILABLE: return "RADIO_UNAVAILABLE";
+            case EVENT_SIM_REFRESH: return "SIM_REFRESH";
+            case EVENT_EID_READY: return "EID_READY";
+            case EVENT_MULTI_SIM_CONFIG_CHANGED: return "MULTI_SIM_CONFIG_CHANGED";
+            default: return "UNKNOWN(" + event + ")";
+        }
+    }
+
     // Easy to use API
     @UnsupportedAppUsage
     public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
@@ -654,7 +675,7 @@
 
         IccCardStatus status = (IccCardStatus)ar.result;
 
-        sLocalLog.log("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
+        logWithLocalLog("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
 
         int slotId = status.physicalSlotIndex;
         if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
@@ -714,10 +735,8 @@
                 if (mDefaultEuiccCardId == UNINITIALIZED_CARD_ID
                         || mDefaultEuiccCardId == TEMPORARILY_UNSUPPORTED_CARD_ID) {
                     mDefaultEuiccCardId = convertToPublicCardId(cardString);
-                    String logStr = "IccCardStatus eid=" + cardString + " slot=" + slotId
-                            + " mDefaultEuiccCardId=" + mDefaultEuiccCardId;
-                    sLocalLog.log(logStr);
-                    log(logStr);
+                    logWithLocalLog("IccCardStatus eid=" + cardString + " slot=" + slotId
+                            + " mDefaultEuiccCardId=" + mDefaultEuiccCardId);
                 }
             }
         }
@@ -868,20 +887,15 @@
         }
         Throwable e = ar.exception;
         if (e != null) {
-            String logStr;
             if (!(e instanceof CommandException) || ((CommandException) e).getCommandError()
                     != CommandException.Error.REQUEST_NOT_SUPPORTED) {
                 // this is not expected; there should be no exception other than
                 // REQUEST_NOT_SUPPORTED
-                logStr = "Unexpected error getting slot status: " + ar.exception;
-                Rlog.e(LOG_TAG, logStr);
-                sLocalLog.log(logStr);
+                logeWithLocalLog("Unexpected error getting slot status: " + ar.exception);
             } else {
                 // REQUEST_NOT_SUPPORTED
-                logStr = "onGetSlotStatusDone: request not supported; marking "
-                        + "mIsSlotStatusSupported to false";
-                log(logStr);
-                sLocalLog.log(logStr);
+                logWithLocalLog("onGetSlotStatusDone: request not supported; marking "
+                        + "mIsSlotStatusSupported to false");
                 mIsSlotStatusSupported = false;
             }
             return;
@@ -893,6 +907,7 @@
             log("onGetSlotStatusDone: No change in slot status");
             return;
         }
+        logWithLocalLog("onGetSlotStatusDone: " + status);
 
         sLastSlotStatus = status;
 
@@ -903,10 +918,8 @@
 
         int numSlots = status.size();
         if (mUiccSlots.length < numSlots) {
-            String logStr = "The number of the physical slots reported " + numSlots
-                    + " is greater than the expectation " + mUiccSlots.length + ".";
-            Rlog.e(LOG_TAG, logStr);
-            sLocalLog.log(logStr);
+            logeWithLocalLog("The number of the physical slots reported " + numSlots
+                    + " is greater than the expectation " + mUiccSlots.length);
             numSlots = mUiccSlots.length;
         }
 
@@ -920,7 +933,6 @@
                 if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
                     Rlog.e(LOG_TAG, "Skipping slot " + i + " as phone " + iss.logicalSlotIndex
                                + " is not available to communicate with this slot");
-
                 } else {
                     mPhoneIdToSlotId[iss.logicalSlotIndex] = i;
                 }
@@ -961,10 +973,8 @@
                 if (!mUiccSlots[i].isRemovable() && !isDefaultEuiccCardIdSet) {
                     isDefaultEuiccCardIdSet = true;
                     mDefaultEuiccCardId = convertToPublicCardId(eid);
-                    String logStr = "Using eid=" + eid + " in slot=" + i
-                            + " to set mDefaultEuiccCardId=" + mDefaultEuiccCardId;
-                    sLocalLog.log(logStr);
-                    log(logStr);
+                    logWithLocalLog("Using eid=" + eid + " in slot=" + i
+                            + " to set mDefaultEuiccCardId=" + mDefaultEuiccCardId);
                 }
             }
         }
@@ -1059,8 +1069,7 @@
         }
 
         IccRefreshResponse resp = (IccRefreshResponse) ar.result;
-        log("onSimRefresh: " + resp);
-        sLocalLog.log("onSimRefresh: " + resp);
+        logWithLocalLog("onSimRefresh: index " + index + ", " + resp);
 
         if (resp == null) {
             Rlog.e(LOG_TAG, "onSimRefresh: received without input");
@@ -1133,18 +1142,14 @@
                 || mDefaultEuiccCardId == TEMPORARILY_UNSUPPORTED_CARD_ID) {
             if (!mUiccSlots[slotId].isRemovable()) {
                 mDefaultEuiccCardId = convertToPublicCardId(eid);
-                String logStr = "onEidReady: eid=" + eid + " slot=" + slotId
-                        + " mDefaultEuiccCardId=" + mDefaultEuiccCardId;
-                sLocalLog.log(logStr);
-                log(logStr);
+                logWithLocalLog("onEidReady: eid=" + eid + " slot=" + slotId
+                        + " mDefaultEuiccCardId=" + mDefaultEuiccCardId);
             } else if (!mHasActiveBuiltInEuicc) {
                 // we only set a removable eUICC to the default if there are no active non-removable
                 // eUICCs
                 mDefaultEuiccCardId = convertToPublicCardId(eid);
-                String logStr = "onEidReady: eid=" + eid + " from removable eUICC in slot="
-                        + slotId + " mDefaultEuiccCardId=" + mDefaultEuiccCardId;
-                sLocalLog.log(logStr);
-                log(logStr);
+                logWithLocalLog("onEidReady: eid=" + eid + " from removable eUICC in slot=" + slotId
+                        + " mDefaultEuiccCardId=" + mDefaultEuiccCardId);
             }
         }
         card.unregisterForEidReady(this);
@@ -1192,7 +1197,18 @@
         Rlog.d(LOG_TAG, string);
     }
 
-    public void addCardLog(String data) {
+    private void logWithLocalLog(String string) {
+        Rlog.d(LOG_TAG, string);
+        sLocalLog.log("UiccController: " + string);
+    }
+
+    private void logeWithLocalLog(String string) {
+        Rlog.e(LOG_TAG, string);
+        sLocalLog.log("UiccController: " + string);
+    }
+
+    /** The supplied log should also indicate the caller to avoid ambiguity. */
+    public static void addLocalLog(String data) {
         sLocalLog.log(data);
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 441fc7a..454792e 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -127,6 +127,7 @@
     private static final int EVENT_SIM_IO_DONE = 12;
     private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 13;
     private static final int EVENT_CARRIER_CONFIG_CHANGED = 14;
+    // NOTE: any new EVENT_* values must be added to eventToString.
 
     private TelephonyManager mTelephonyManager;
 
@@ -180,6 +181,7 @@
     public final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
+            String eventName = eventToString(msg.what);
             // We still need to handle the following response messages even the UiccProfile has been
             // disposed because whoever sent the request may be still waiting for the response.
             if (mDisposed && msg.what != EVENT_OPEN_LOGICAL_CHANNEL_DONE
@@ -187,11 +189,11 @@
                     && msg.what != EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE
                     && msg.what != EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE
                     && msg.what != EVENT_SIM_IO_DONE) {
-                loge("handleMessage: Received " + msg.what
+                loge("handleMessage: Received " + eventName
                         + " after dispose(); ignoring the message");
                 return;
             }
-            loglocal("handleMessage: Received " + msg.what + " for phoneId " + mPhoneId);
+            logWithLocalLog("handleMessage: Received " + eventName + " for phoneId " + mPhoneId);
             switch (msg.what) {
                 case EVENT_NETWORK_LOCKED:
                     mNetworkLockedRegistrants.notifyRegistrants();
@@ -201,7 +203,7 @@
                 case EVENT_APP_READY:
                 case EVENT_RECORDS_LOADED:
                 case EVENT_EID_READY:
-                    if (VDBG) log("handleMessage: Received " + msg.what);
+                    if (VDBG) log("handleMessage: Received " + eventName);
                     updateExternalState();
                     break;
 
@@ -234,8 +236,8 @@
                 case EVENT_SIM_IO_DONE:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     if (ar.exception != null) {
-                        loglocal("handleMessage: Exception " + ar.exception);
-                        log("handleMessage: Error in SIM access with exception" + ar.exception);
+                        logWithLocalLog("handleMessage: Error in SIM access with exception "
+                                + ar.exception);
                     }
                     AsyncResult.forMessage((Message) ar.userObj, ar.result, ar.exception);
                     ((Message) ar.userObj).sendToTarget();
@@ -1469,8 +1471,8 @@
      * Exposes {@link CommandsInterface#iccOpenLogicalChannel}
      */
     public void iccOpenLogicalChannel(String aid, int p2, Message response) {
-        loglocal("iccOpenLogicalChannel: " + aid + " , " + p2 + " by pid:" + Binder.getCallingPid()
-                + " uid:" + Binder.getCallingUid());
+        logWithLocalLog("iccOpenLogicalChannel: " + aid + " , " + p2 + " by pid:"
+                + Binder.getCallingPid() + " uid:" + Binder.getCallingUid());
         mCi.iccOpenLogicalChannel(aid, p2,
                 mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
     }
@@ -1479,7 +1481,7 @@
      * Exposes {@link CommandsInterface#iccCloseLogicalChannel}
      */
     public void iccCloseLogicalChannel(int channel, Message response) {
-        loglocal("iccCloseLogicalChannel: " + channel);
+        logWithLocalLog("iccCloseLogicalChannel: " + channel);
         mCi.iccCloseLogicalChannel(channel,
                 mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
     }
@@ -1694,6 +1696,26 @@
         return null;
     }
 
+    private static String eventToString(int event) {
+        switch (event) {
+            case EVENT_RADIO_OFF_OR_UNAVAILABLE: return "RADIO_OFF_OR_UNAVAILABLE";
+            case EVENT_ICC_LOCKED: return "ICC_LOCKED";
+            case EVENT_APP_READY: return "APP_READY";
+            case EVENT_RECORDS_LOADED: return "RECORDS_LOADED";
+            case EVENT_NETWORK_LOCKED: return "NETWORK_LOCKED";
+            case EVENT_EID_READY: return "EID_READY";
+            case EVENT_ICC_RECORD_EVENTS: return "ICC_RECORD_EVENTS";
+            case EVENT_OPEN_LOGICAL_CHANNEL_DONE: return "OPEN_LOGICAL_CHANNEL_DONE";
+            case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: return "CLOSE_LOGICAL_CHANNEL_DONE";
+            case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE: return "TRANSMIT_APDU_LOGICAL_CHANNEL_DONE";
+            case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE: return "TRANSMIT_APDU_BASIC_CHANNEL_DONE";
+            case EVENT_SIM_IO_DONE: return "SIM_IO_DONE";
+            case EVENT_CARRIER_PRIVILEGES_LOADED: return "CARRIER_PRIVILEGES_LOADED";
+            case EVENT_CARRIER_CONFIG_CHANGED: return "CARRIER_CONFIG_CHANGED";
+            default: return "UNKNOWN(" + event + ")";
+        }
+    }
+
     private static void log(String msg) {
         Rlog.d(LOG_TAG, msg);
     }
@@ -1702,8 +1724,9 @@
         Rlog.e(LOG_TAG, msg);
     }
 
-    private void loglocal(String msg) {
-        if (DBG) UiccController.sLocalLog.log("UiccProfile[" + mPhoneId + "]: " + msg);
+    private void logWithLocalLog(String msg) {
+        Rlog.d(LOG_TAG, msg);
+        if (DBG) UiccController.addLocalLog("UiccProfile[" + mPhoneId + "]: " + msg);
     }
 
     /**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
index a10dc9b..afb0292 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
@@ -76,7 +76,6 @@
         super.setUp(getClass().getSimpleName());
         if (Looper.myLooper() == null) {
             Looper.prepare();
-            Looper.loop();
         }
         mCarrierServicesSmsFilterUT = new CarrierServicesSmsFilter(
                 mContext, mPhone, new byte[][]{SMS_PDU},
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
index 6baf151..ac32b91 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
@@ -22,9 +22,7 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 
 /** Unit tests for {@link CellIdentityLte}. */
 
@@ -38,7 +36,7 @@
     private static final int TAC = 65535;
     // Absolute RF Channel Number ranges from 0 to 262140.
     private static final int EARFCN = 262140;
-    private static final List<Integer> BANDS = Arrays.asList(1, 2);
+    private static final int[] BANDS = new int[] {1, 2};
     private static final int MCC = 120;
     private static final int MNC = 260;
     private static final int BANDWIDTH = 5000;  // kHz
@@ -218,7 +216,7 @@
         p.writeInt(PCI);
         p.writeInt(TAC);
         p.writeInt(EARFCN);
-        p.writeList(BANDS);
+        p.writeIntArray(BANDS);
         p.writeInt(BANDWIDTH);
         p.setDataPosition(0);
 
@@ -244,7 +242,7 @@
         p.writeInt(PCI);
         p.writeInt(TAC);
         p.writeInt(EARFCN);
-        p.writeList(BANDS);
+        p.writeIntArray(BANDS);
         p.writeInt(BANDWIDTH);
         p.setDataPosition(0);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java
index 28dc81b..874554c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityNrTest.java
@@ -26,10 +26,8 @@
 
 import org.junit.Test;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 
 public class CellIdentityNrTest extends AndroidTestCase {
     private static final String MCC = "310";
@@ -42,8 +40,10 @@
     private static final int PCI = 123;
     private static final int TAC = 32767;
     private static final int NCI = 8675309;
-    private static final List<Integer> BANDS = new ArrayList<>(Arrays.asList(
-            AccessNetworkConstants.NgranBands.BAND_1, AccessNetworkConstants.NgranBands.BAND_1));
+    private static final int[] BANDS = new int[] {
+            AccessNetworkConstants.NgranBands.BAND_1,
+            AccessNetworkConstants.NgranBands.BAND_2
+    };
 
     @Test
     public void testGetMethod() {
@@ -112,7 +112,7 @@
         assertThat(anotherCellIdentityNr.getNrarfcn()).isEqualTo(NRARFCN);
         assertThat(anotherCellIdentityNr.getPci()).isEqualTo(PCI);
         assertThat(anotherCellIdentityNr.getTac()).isEqualTo(TAC);
-        assertThat(anotherCellIdentityNr.getBands()).isEqualTo(BANDS);
+        assertTrue(Arrays.equals(anotherCellIdentityNr.getBands(), BANDS));
         assertThat(anotherCellIdentityNr.getOperatorAlphaLong()).isEqualTo(ALPHAL);
         assertThat(anotherCellIdentityNr.getOperatorAlphaShort()).isEqualTo(ALPHAS);
         assertThat(anotherCellIdentityNr.getMccString()).isEqualTo(MCC);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java
index a506397..ae07e76 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTest.java
@@ -25,9 +25,7 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 
 public class CellIdentityTest extends AndroidTestCase {
 
@@ -39,7 +37,7 @@
     private static final int TAC = 65535;
     // Absolute RF Channel Number ranges from 0 to 262140.
     private static final int EARFCN = 262140;
-    private static final List<Integer> BANDS = Arrays.asList(1, 2);
+    private static final int[] BANDS = new int[] {1, 2};
     private static final int BANDWIDTH = 5000;  // kHz
     private static final int MCC = 120;
     private static final int MNC = 260;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
index 2b8643e..f473133 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
@@ -32,7 +32,6 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 
 /** Unit tests for {@link NetworkScanResult}. */
@@ -54,7 +53,7 @@
         infos.add(gsm);
 
         CellIdentityLte cil = new CellIdentityLte(
-                10, 5, 200, 2000, Arrays.asList(1, 2), 10000, "001", "01", "test", "tst",
+                10, 5, 200, 2000, new int[] {1, 2}, 10000, "001", "01", "test", "tst",
                 Collections.emptyList(), null);
         CellSignalStrengthLte cssl = new CellSignalStrengthLte(15, 16, 17, 18, 19, 20);
         CellInfoLte lte = new CellInfoLte();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 76d2f02..34e2725 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -1189,7 +1189,7 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, Collections.emptyList(),
+        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, new int[] {},
                 Integer.MAX_VALUE, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
                 Collections.emptyList(), null);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1379,7 +1379,7 @@
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
         CellIdentityLte cil = new CellIdentityLte(
-                CI, PCI, TAC, EARFCN, Collections.emptyList(), BANDWIDTH, MCC_STR, MNC_STR,
+                CI, PCI, TAC, EARFCN, new int[] {}, BANDWIDTH, MCC_STR, MNC_STR,
                 ALPHA_LONG, ALPHA_SHORT, Collections.emptyList(), null);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
                 RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
@@ -1400,7 +1400,7 @@
         CellInfoLte expected = new CellInfoLte();
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
-        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, Collections.emptyList(),
+        CellIdentityLte cil = new CellIdentityLte(CI, PCI, TAC, EARFCN, new int[] {},
                 BANDWIDTH, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT,
                 Collections.emptyList(), null);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
@@ -1425,7 +1425,7 @@
         expected.setRegistered(false);
         expected.setTimeStamp(TIMESTAMP);
         CellIdentityLte cil = new CellIdentityLte(
-                CI, PCI, TAC, EARFCN, Collections.emptyList(), BANDWIDTH, null, null, ALPHA_LONG,
+                CI, PCI, TAC, EARFCN, new int[] {}, BANDWIDTH, null, null, ALPHA_LONG,
                 ALPHA_SHORT, Collections.emptyList(), null);
         CellSignalStrengthLte css = new CellSignalStrengthLte(
                 RSSI, RSRP, RSRQ, RSSNR, CQI, TIMING_ADVANCE);
@@ -1646,7 +1646,7 @@
                 (CellSignalStrengthNr) cellInfoNr.getCellSignalStrength();
 
         CellIdentityNr expectedCellIdentity = new CellIdentityNr(PCI, TAC, NRARFCN,
-                Collections.emptyList(), MCC_STR, MNC_STR, CI, ALPHA_LONG, ALPHA_SHORT,
+                new int[] {}, MCC_STR, MNC_STR, CI, ALPHA_LONG, ALPHA_SHORT,
                 Collections.emptyList());
         CellSignalStrengthNr expectedSignalStrength = new CellSignalStrengthNr(-RSRP, -RSRQ,
                 SIGNAL_NOISE_RATIO, -RSRP, -RSRQ, SIGNAL_NOISE_RATIO);
@@ -1713,6 +1713,60 @@
         result14.mtu = 1500;
 
         assertEquals(response, RIL.convertDataCallResult(result14));
+
+        // Test V1.5 SetupDataCallResult
+        android.hardware.radio.V1_5.SetupDataCallResult result15 =
+                new android.hardware.radio.V1_5.SetupDataCallResult();
+        result15.cause = android.hardware.radio.V1_4.DataCallFailCause.NONE;
+        result15.suggestedRetryTime = -1;
+        result15.cid = 0;
+        result15.active = android.hardware.radio.V1_4.DataConnActiveStatus.ACTIVE;
+        result15.type = android.hardware.radio.V1_4.PdpProtocolType.IPV4V6;
+        result15.ifname = "ifname";
+
+        android.hardware.radio.V1_5.LinkAddress la1 = new android.hardware.radio.V1_5.LinkAddress();
+        la1.address = "10.0.2.15";
+        la1.properties = 0;
+        la1.deprecationTime = -1;
+        la1.expirationTime = -1;
+
+        android.hardware.radio.V1_5.LinkAddress la2 = new android.hardware.radio.V1_5.LinkAddress();
+        la2.address = "2607:fb90:a620:651d:eabe:f8da:c107:44be/64";
+        la2.properties = 0;
+        la2.deprecationTime = -1;
+        la2.expirationTime = -1;
+        result15.addresses = new ArrayList<>(Arrays.asList(la1, la2));
+        result15.dnses = new ArrayList<>(Arrays.asList("10.0.2.3", "fd00:976a::9"));
+        result15.gateways = new ArrayList<>(Arrays.asList("10.0.2.15", "fe80::2"));
+        result15.pcscf = new ArrayList<>(Arrays.asList(
+                "fd00:976a:c206:20::6", "fd00:976a:c206:20::9", "fd00:976a:c202:1d::9"));
+        result15.mtuV4 = 1500;
+        result15.mtuV6 = 3000;
+
+        response = new DataCallResponse.Builder()
+                .setCause(0)
+                .setSuggestedRetryTime(-1)
+                .setId(0)
+                .setLinkStatus(2)
+                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+                .setInterfaceName("ifname")
+                .setAddresses(Arrays.asList(
+                        new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
+                        new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
+                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+                        InetAddresses.parseNumericAddress("fd00:976a::9")))
+                .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.15"),
+                        InetAddresses.parseNumericAddress("fe80::2")))
+                .setPcscfAddresses(Arrays.asList(
+                        InetAddresses.parseNumericAddress("fd00:976a:c206:20::6"),
+                        InetAddresses.parseNumericAddress("fd00:976a:c206:20::9"),
+                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::9")))
+                .setMtuV4(1500)
+                .setMtuV6(3000)
+                .setVersion(5)
+                .build();
+
+        assertEquals(response, RIL.convertDataCallResult(result15));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioAccessFamilyTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessFamilyTest.java
new file mode 100644
index 0000000..83c362b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessFamilyTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.telephony.RadioAccessFamily;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+public class RadioAccessFamilyTest extends TelephonyTest {
+    @Test
+    @SmallTest
+    public void testCompareSameFamily() throws Exception {
+        // same family same number results in no clear winner
+        assertEquals(0, RadioAccessFamily.compare(
+                    TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA,
+                    TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA));
+
+        // same family, return the one with more total bits, in this case RHS,
+        // so compare should be negative.
+        assertTrue(0 > RadioAccessFamily.compare(
+                TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA,
+                TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA
+                        | TelephonyManager.NETWORK_TYPE_BITMASK_HSPA));
+    }
+
+    @Test
+    @SmallTest
+    public void testComparedGreatestUnique() throws Exception {
+        // Because LHS supports a unique higher-generation RAT, prefer that to a large list of
+        // older RATs. Since RHS is greater, compare should be positive.
+        assertTrue(0 < RadioAccessFamily.compare(
+                TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                        | TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA,
+                TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                        | TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA
+                        | TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA
+                        | TelephonyManager.NETWORK_TYPE_BITMASK_HSPA));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 4b7835a..dc935e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -2087,7 +2087,7 @@
     public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
         // Cell ID change should trigger hasLocationChanged.
         CellIdentityLte cellIdentity5 =
-                new CellIdentityLte(1, 1, 5, 1, Arrays.asList(1, 2), 5000, "001", "01", "test",
+                new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
                         "tst", Collections.emptyList(), null);
 
         sendPhyChanConfigChange(new int[] {10000});
@@ -2099,7 +2099,7 @@
     public void testPhyChanBandwidthNotUpdatedWhenInvalidInCellIdentity() throws Exception {
         // Cell ID change should trigger hasLocationChanged.
         CellIdentityLte cellIdentityInv =
-                new CellIdentityLte(1, 1, 5, 1, Arrays.asList(1, 2), 12345, "001", "01", "test",
+                new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 12345, "001", "01", "test",
                         "tst", Collections.emptyList(), null);
 
         sendPhyChanConfigChange(new int[] {10000});
@@ -2111,7 +2111,7 @@
     public void testPhyChanBandwidthPrefersCarrierAggregationReport() throws Exception {
         // Cell ID change should trigger hasLocationChanged.
         CellIdentityLte cellIdentity10 =
-                new CellIdentityLte(1, 1, 5, 1, Arrays.asList(1, 2), 10000, "001", "01", "test",
+                new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 10000, "001", "01", "test",
                         "tst", Collections.emptyList(), null);
 
         sendPhyChanConfigChange(new int[] {10000, 5000});
@@ -2123,7 +2123,7 @@
     public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
         // LTE Cell with bandwidth = 10000
         CellIdentityLte cellIdentity10 =
-                new CellIdentityLte(1, 1, 1, 1, Arrays.asList(1, 2), 10000, "1", "1", "test",
+                new CellIdentityLte(1, 1, 1, 1, new int[] {1, 2}, 10000, "1", "1", "test",
                         "tst", Collections.emptyList(), null);
 
         sendRegStateUpdateForLteCellId(cellIdentity10);
@@ -2171,7 +2171,7 @@
 
         // Start state: Cell data only LTE + IWLAN
         CellIdentityLte cellIdentity =
-                new CellIdentityLte(1, 1, 5, 1, Arrays.asList(1, 2), 5000, "001", "01", "test",
+                new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
                         "tst", Collections.emptyList(), null);
         changeRegStateWithIwlan(
                 // WWAN
@@ -2327,7 +2327,7 @@
         sst.mSS = ss;
 
         CellIdentityLte cellId =
-                new CellIdentityLte(1, 1, 5, 1, Arrays.asList(1, 2), 5000, "001", "01", "test",
+                new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
                         "tst", Collections.emptyList(), null);
         LteVopsSupportInfo lteVopsSupportInfo =
                 new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
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 261e0d0..da9b7bd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -1344,6 +1344,7 @@
 
     // Test update waiting apn list when on data rat change
     @FlakyTest /* flakes 0.86% of the time */
+    @Ignore
     @Test
     @SmallTest
     public void testUpdateWaitingApnListOnDataRatChange() throws Exception {
@@ -1820,7 +1821,6 @@
             plans.add(SubscriptionPlan.Builder
                     .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
                             Period.ofMonths(1))
-                    .setTitle("Some NR 5G unmetered workaround plan")
                     .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
                             SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
                     .setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_NR})
@@ -1829,7 +1829,6 @@
         plans.add(SubscriptionPlan.Builder
                 .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
                         Period.ofMonths(1))
-                .setTitle("Some 5GB Plan")
                 .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
                 .setDataUsage(500_000_000, System.currentTimeMillis())
                 .build());
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 2e1c3eb..92a0c7e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -46,6 +46,7 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsService;
@@ -54,6 +55,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.telephony.PhoneConfigurationManager;
@@ -68,6 +70,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -89,6 +92,7 @@
             "TestCarrier2Pkg", "Carrier2ImsService");
 
     private static final int NUM_MAX_SLOTS = 2;
+    private static final String TAG = ImsResolverTest.class.getSimpleName();
 
     @Mock
     Context mMockContext;
@@ -101,6 +105,8 @@
     @Mock
     CarrierConfigManager mMockCarrierConfigManager;
     @Mock
+    UserManager mMockUserManager;
+    @Mock
     ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
     @Mock
     ImsServiceFeatureQueryManager mMockQueryManager;
@@ -550,6 +556,61 @@
     }
 
     /**
+     * Test that the dynamic ims services are bound in the event that the user is not yet unlocked
+     * but the carrier config changed event is fired.
+     * @throws RemoteException
+     */
+    @Test
+    @SmallTest
+    public void testDeviceDynamicQueryBindsOnCarrierConfigChanged() throws RemoteException {
+        //Set package names with no features set in metadata
+        List<ResolveInfo> info = new ArrayList<>();
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, new HashSet<>(), true));
+        info.add(getResolveInfo(TEST_DEVICE2_DEFAULT_NAME, new HashSet<>(), true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+        setupPackageQuery(info);
+
+        //setupResolver
+        setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+                TEST_DEVICE2_DEFAULT_NAME.getPackageName());
+
+        //Set controllers
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController deviceController2 = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+
+        Map<String, ImsServiceController> controllerMap = new ArrayMap<>();
+        controllerMap.put(TEST_DEVICE_DEFAULT_NAME.getPackageName(), deviceController);
+        controllerMap.put(TEST_DEVICE2_DEFAULT_NAME.getPackageName(), deviceController2);
+        controllerMap.put(TEST_CARRIER_DEFAULT_NAME.getPackageName(), carrierController);
+        setImsServiceControllerFactory(controllerMap);
+
+        //Set features to device ims services
+        Set<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures1 =
+                convertToFeatureSlotPairs(0, ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE,
+                        ImsResolver.METADATA_MMTEL_FEATURE);
+
+        Set<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures2 =
+                convertToFeatureSlotPairs(0, ImsResolver.METADATA_RCS_FEATURE);
+
+        startBindNoCarrierConfig(1);
+        mLooper.processAllMessages();
+        // ensure that startQuery was called
+        verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE_DEFAULT_NAME),
+                any(String.class));
+
+        verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE2_DEFAULT_NAME),
+                any(String.class));
+
+        mDynamicQueryListener.onComplete(TEST_DEVICE_DEFAULT_NAME, deviceFeatures1);
+        mDynamicQueryListener.onComplete(TEST_DEVICE2_DEFAULT_NAME, deviceFeatures2);
+        mLooper.processAllMessages();
+
+        verify(deviceController, times(2)).bind(eq(deviceFeatures1));
+        verify(deviceController2, times(1)).bind(eq(deviceFeatures2));
+    }
+
+    /**
      * Test that when a device and carrier override package are set, both ImsServices are bound.
      * Verify that the carrier ImsService features are created and the device default features
      * are created for all features that are not covered by the carrier ImsService. When the device
@@ -1581,6 +1642,12 @@
         when(mMockContext.createContextAsUser(any(), eq(0))).thenReturn(mMockContext);
         when(mMockContext.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE))).thenReturn(
                 mMockCarrierConfigManager);
+        when(mMockContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mMockUserManager);
+
+        //If this is not false, then HANDLER_BOOT_COMPLETE is fired now but the controller factories
+        //used in the test methods aren't created in time.
+        when(mMockUserManager.isUserUnlocked()).thenReturn(false);
+
         // Support configs for MSIM always in case we are testing dynamic sim slot config changes.
         mCarrierConfigs = new PersistableBundle[NUM_MAX_SLOTS];
         for (int i = 0; i < NUM_MAX_SLOTS; i++) {
@@ -1719,6 +1786,22 @@
         mLooper.processAllMessages();
     }
 
+    private void setImsServiceControllerFactory(Map<String, ImsServiceController> controllerMap) {
+        mTestImsResolver.setImsServiceControllerFactory(
+                new ImsResolver.ImsServiceControllerFactory() {
+                    @Override
+                    public String getServiceInterface() {
+                        return ImsService.SERVICE_INTERFACE;
+                    }
+
+                    @Override
+                    public ImsServiceController create(Context context, ComponentName componentName,
+                            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+                        return controllerMap.get(componentName.getPackageName());
+                    }
+                });
+    }
+
     private void setImsServiceControllerFactory(ImsServiceController deviceController,
             ImsServiceController carrierController) {
         mTestImsResolver.setImsServiceControllerFactory(
@@ -1844,6 +1927,11 @@
                 .collect(Collectors.toCollection(HashSet::new));
     }
 
+    private HashSet<ImsFeatureConfiguration.FeatureSlotPair> convertToFeatureSlotPairs(
+            int slotId, String... features) {
+        return convertToHashSet(new ArraySet<>(features), slotId);
+    }
+
     private int metadataStringToFeature(String f) {
         switch (f) {
             case ImsResolver.METADATA_MMTEL_FEATURE: