Merge "Create a single instance of CarrierPrivilegesTracker"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index dd68591..8fdd16b 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -4051,13 +4051,19 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(
-            int signalStrengthMeasure, int[] thresholds, int ran, boolean isEnabled) {
+    public void setSignalStrengthReportingCriteria(int signalStrengthMeasure,
+            int[] systemThresholds, int ran, boolean isEnabledForSystem) {
         int[] consolidatedThresholds = mSST.getConsolidatedSignalThresholds(
                 ran,
                 signalStrengthMeasure,
-                mSST.shouldHonorSystemThresholds() ? thresholds : new int[]{},
+                isEnabledForSystem && mSST.shouldHonorSystemThresholds() ? systemThresholds
+                        : new int[]{},
                 REPORTING_HYSTERESIS_DB);
+        boolean isEnabledForAppRequest = mSST.shouldEnableSignalThresholdForAppRequest(
+                ran,
+                signalStrengthMeasure,
+                getSubId(),
+                isDeviceIdle());
         mCi.setSignalStrengthReportingCriteria(
                 new SignalThresholdInfo.Builder()
                         .setRadioAccessNetworkType(ran)
@@ -4065,7 +4071,7 @@
                         .setHysteresisMs(REPORTING_HYSTERESIS_MILLIS)
                         .setHysteresisDb(REPORTING_HYSTERESIS_DB)
                         .setThresholds(consolidatedThresholds, true /*isSystem*/)
-                        .setIsEnabled(isEnabled)
+                        .setIsEnabled(isEnabledForSystem || isEnabledForAppRequest)
                         .build(),
                 ran, null);
     }
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index dc32ba5..949feee 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -82,6 +82,8 @@
     private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
     private static final int EVENT_CARRIER_CONFIG_CHANGED            = 7;
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED          = 8;
+    @VisibleForTesting
+    public static final int EVENT_RADIO_STATE_CHANGED                = 9;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"PRIMARY_SUB_"},
@@ -294,6 +296,16 @@
             case EVENT_MULTI_SIM_CONFIG_CHANGED:
                 int activeModems = (int) ((AsyncResult) msg.obj).result;
                 onMultiSimConfigChanged(activeModems);
+                break;
+            case EVENT_RADIO_STATE_CHANGED:
+                for (Phone phone : PhoneFactory.getPhones()) {
+                    if (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+                        if (DBG) log("Radio unavailable. Clearing sub info initialized flag.");
+                        mSubInfoInitialized = false;
+                        break;
+                    }
+                }
+                break;
         }
     }
 
@@ -335,6 +347,9 @@
     private void onAllSubscriptionsLoaded() {
         if (DBG) log("onAllSubscriptionsLoaded");
         mSubInfoInitialized = true;
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+        }
         reEvaluateAll();
     }
 
@@ -424,6 +439,9 @@
         for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) {
             mCarrierConfigLoadedSubIds[phoneId] = INVALID_SUBSCRIPTION_ID;
         }
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index edd49b1..5d5fca0 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -27,15 +27,19 @@
 import android.telephony.Annotation;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PcoData;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.dataconnection.DcController;
 import com.android.internal.telephony.dataconnection.DcController.PhysicalLinkState;
+import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.util.IState;
 import com.android.internal.util.IndentingPrintWriter;
@@ -93,8 +97,9 @@
     private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
     private static final int EVENT_INITIALIZE = 12;
     private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
+    private static final int EVENT_PCO_DATA_CHANGED = 14;
 
-    private static final String[] sEvents = new String[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED + 1];
+    private static final String[] sEvents = new String[EVENT_PCO_DATA_CHANGED + 1];
     static {
         sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
         sEvents[EVENT_QUIT] = "EVENT_QUIT";
@@ -111,6 +116,7 @@
         sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
         sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
+        sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED";
     }
 
     private final Phone mPhone;
@@ -141,6 +147,9 @@
     private String mSecondaryTimerState;
     private String mPreviousState;
     private @PhysicalLinkState int mPhysicalLinkState;
+    private boolean mIsPhysicalChannelConfig16Supported;
+    private Boolean mIsNrAdvancedAllowedByPco = false;
+    private int mNrAdvancedCapablePcoId = 0;
 
     /**
      * NetworkTypeController constructor.
@@ -190,9 +199,14 @@
         mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
                 EVENT_DATA_RAT_CHANGED, null);
-        mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                .registerForPhysicalLinkStateChanged(getHandler(),
-                        EVENT_PHYSICAL_LINK_STATE_CHANGED);
+        mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
+                TelephonyManager.class).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+        if (!mIsPhysicalChannelConfig16Supported) {
+            mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .registerForPhysicalLinkStateChanged(getHandler(),
+                            EVENT_PHYSICAL_LINK_STATE_CHANGED);
+        }
         mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
                 EVENT_NR_STATE_CHANGED, null);
         mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
@@ -202,6 +216,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
+        mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null);
     }
 
     private void unRegisterForAllEvents() {
@@ -213,6 +228,7 @@
         mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
         mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
         mPhone.getContext().unregisterReceiver(mIntentReceiver);
+        mPhone.mCi.unregisterForPcoData(getHandler());
     }
 
     private void parseCarrierConfigs() {
@@ -261,6 +277,8 @@
                         mLtePlusThresholdBandwidth);
                 mAdditionalNrAdvancedBandsList = b.getIntArray(
                         CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
+                mNrAdvancedCapablePcoId = b.getInt(
+                        CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
             }
         }
         createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
@@ -481,9 +499,14 @@
                 case EVENT_DATA_RAT_CHANGED:
                 case EVENT_NR_STATE_CHANGED:
                 case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                case EVENT_PCO_DATA_CHANGED:
                     // ignored
                     break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    if (mIsPhysicalChannelConfig16Supported) {
+                        mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+                    }
+                    break;
                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkState = (int) ar.result;
@@ -582,9 +605,18 @@
                     mIsNrRestricted = isNrRestricted();
                     break;
                 case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
                     // ignored
                     break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    if (mIsPhysicalChannelConfig16Supported) {
+                        mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+                        if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+                            resetAllTimers();
+                        }
+                    }
+                    // Update in case of LTE/LTE+ switch
+                    updateOverrideNetworkType();
+                    break;
                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkState = (int) ar.result;
@@ -645,9 +677,25 @@
                     }
                     break;
                 case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
                     // ignore
                     break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    if (mIsPhysicalChannelConfig16Supported) {
+                        mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+                        if (isNrNotRestricted()) {
+                            // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
+                            if (isPhysicalLinkActive()) {
+                                transitionWithTimerTo(mLteConnectedState);
+                                break;
+                            }
+                        } else {
+                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+                            sendMessage(EVENT_NR_STATE_CHANGED);
+                        }
+                    }
+                    // Update in case of LTE/LTE+ switch
+                    updateOverrideNetworkType();
+                    break;
                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkState = (int) ar.result;
@@ -713,9 +761,25 @@
                     }
                     break;
                 case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
                     // ignore
                     break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    if (mIsPhysicalChannelConfig16Supported) {
+                        mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+                        if (isNrNotRestricted()) {
+                            // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
+                            if (!isPhysicalLinkActive()) {
+                                transitionWithTimerTo(mIdleState);
+                                break;
+                            }
+                        } else {
+                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+                            sendMessage(EVENT_NR_STATE_CHANGED);
+                        }
+                    }
+                    // Update in case of LTE/LTE+ switch
+                    updateOverrideNetworkType();
+                    break;
                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkState = (int) ar.result;
@@ -787,21 +851,15 @@
                         transitionWithTimerTo(mLegacyState);
                     }
                     break;
+                case EVENT_PCO_DATA_CHANGED:
+                    handlePcoData((AsyncResult) msg.obj);
+                    break;
                 case EVENT_NR_FREQUENCY_CHANGED:
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (!isNrConnected()) {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
-                        break;
+                    if (mIsPhysicalChannelConfig16Supported) {
+                        mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
                     }
-                    if (!isNrAdvanced()) {
-                        // STATE_CONNECTED_NR_ADVANCED -> STATE_CONNECTED
-                        transitionWithTimerTo(mNrConnectedState);
-                    } else {
-                        // STATE_CONNECTED -> STATE_CONNECTED_NR_ADVANCED
-                        transitionTo(mNrConnectedState);
-                    }
-                    mIsNrAdvanced = isNrAdvanced();
+                    updateNrAdvancedState();
                     break;
                 case EVENT_PHYSICAL_LINK_STATE_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
@@ -824,6 +882,47 @@
         public String getName() {
             return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
         }
+
+        private void updateNrAdvancedState() {
+            if (!isNrConnected()) {
+                log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+                sendMessage(EVENT_NR_STATE_CHANGED);
+                return;
+            }
+            if (!isNrAdvanced()) {
+                // STATE_CONNECTED_NR_ADVANCED -> STATE_CONNECTED
+                transitionWithTimerTo(mNrConnectedState);
+            } else {
+                // STATE_CONNECTED -> STATE_CONNECTED_NR_ADVANCED
+                transitionTo(mNrConnectedState);
+            }
+            mIsNrAdvanced = isNrAdvanced();
+        }
+
+        private void handlePcoData(AsyncResult ar) {
+            if (ar.exception != null) {
+                loge("PCO_DATA exception: " + ar.exception);
+                return;
+            }
+            PcoData pcodata = (PcoData) ar.result;
+            if (pcodata == null) {
+                return;
+            }
+            log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata);
+            DcTracker dcTracker = mPhone.getDcTracker(
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            DataConnection dc =
+                    dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null;
+            ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null;
+            if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT)
+                    && mNrAdvancedCapablePcoId > 0
+                    && pcodata.pcoId == mNrAdvancedCapablePcoId
+            ) {
+                log("EVENT_PCO_DATA_CHANGED: Nr Advanced is allowed by PCO.");
+                mIsNrAdvancedAllowedByPco = pcodata.contents[0] == 1;
+                updateNrAdvancedState();
+            }
+        }
     }
 
     private final NrConnectedState mNrConnectedState = new NrConnectedState();
@@ -1027,7 +1126,7 @@
     }
 
     private boolean isNrAdvanced() {
-        return isNrMmwave() || isAdditionalNrAdvancedBand();
+        return isNrAdvancedCapable() && (isNrMmwave() || isAdditionalNrAdvancedBand());
     }
 
     private boolean isNrMmwave() {
@@ -1051,6 +1150,13 @@
         return false;
     }
 
+    private boolean isNrAdvancedCapable() {
+        if (mNrAdvancedCapablePcoId > 0) {
+            return mIsNrAdvancedAllowedByPco;
+        }
+        return true;
+    }
+
     private boolean isLte(int rat) {
         return rat == TelephonyManager.NETWORK_TYPE_LTE
                 || rat == TelephonyManager.NETWORK_TYPE_LTE_CA;
@@ -1060,6 +1166,13 @@
         return mPhysicalLinkState == DcController.PHYSICAL_LINK_ACTIVE;
     }
 
+    private int getPhysicalLinkStateFromPhysicalChannelConfig() {
+        List<PhysicalChannelConfig> physicalChannelConfigList =
+                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+        return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty())
+                ? DcController.PHYSICAL_LINK_NOT_ACTIVE : DcController.PHYSICAL_LINK_ACTIVE;
+    }
+
     private int getDataNetworkType() {
         NetworkRegistrationInfo nri =  mPhone.getServiceState().getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
@@ -1092,7 +1205,8 @@
                 + ", mIsSecondaryTimerActive=" + mIsSecondaryTimerActive
                 + ", mPrimaryTimerState=" + mPrimaryTimerState
                 + ", mSecondaryTimerState=" + mSecondaryTimerState
-                + ", mPreviousState=" + mPreviousState;
+                + ", mPreviousState=" + mPreviousState
+                + ", misNrAdvanced=" + isNrAdvanced();
     }
 
     @Override
@@ -1117,6 +1231,7 @@
         pw.println("mPhysicalLinkState=" + mPhysicalLinkState);
         pw.println("mAdditionalNrAdvancedBandsList="
                 + Arrays.toString(mAdditionalNrAdvancedBandsList));
+        pw.println("mNrAdvancedCapablePcoId=" + mNrAdvancedCapablePcoId);
         pw.decreaseIndent();
         pw.flush();
     }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index f511bae..9e07362 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -187,7 +187,8 @@
     protected static final int EVENT_RUIM_RECORDS_LOADED            = 22;
     protected static final int EVENT_NV_READY                       = 23;
     private static final int EVENT_SET_ENHANCED_VP                  = 24;
-    protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER  = 25;
+    @VisibleForTesting
+    public static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER  = 25;
     protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 26;
     protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 27;
     // other
@@ -1842,6 +1843,17 @@
     }
 
     /**
+     * Check whether the radio is off for thermal reason.
+     *
+     * @return {@code true} only if thermal mitigation is one of the reason for which radio is off.
+     */
+    public boolean isRadioOffForThermalMitigation() {
+        ServiceStateTracker sst = getServiceStateTracker();
+        return sst != null && sst.getRadioPowerOffReasons()
+                .contains(Phone.RADIO_POWER_REASON_THERMAL);
+    }
+
+    /**
      * Retrieves the EmergencyNumberTracker of the phone instance.
      */
     public EmergencyNumberTracker getEmergencyNumberTracker() {
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 598e8db..19c6021 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -433,8 +433,7 @@
         NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
                 builder.build(), this);
         // we want to see all requests
-        networkFactory.setScoreFilter(101);
-        networkFactory.register();
+        networkFactory.registerIgnoringScore();
 
         updateHalCommandToUse();
 
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index bbacbdc..1ec9cff 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -19,6 +19,7 @@
 import static android.telephony.TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED;
 import static android.telephony.TelephonyManager
         .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE;
+import static android.telephony.TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED;
 import static android.telephony.TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE;
 import static android.telephony.TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM;
 import static android.telephony.TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING;
@@ -311,6 +312,8 @@
                 Rlog.d(TAG, "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING");
                 caps.add(CAPABILITY_SIM_PHONEBOOK_IN_MODEM);
                 Rlog.d(TAG, "CAPABILITY_SIM_PHONEBOOK_IN_MODEM");
+                caps.add(CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+                Rlog.d(TAG, "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED");
             }
         }
         return caps;
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
index 99ffe36..de62c70 100644
--- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -123,6 +123,10 @@
                     if (ar.exception != null) {
                         loge("setupRadioInterfaceCapabilities: " + ar.exception);
                     }
+                    if (ar.result == null) {
+                        loge("setupRadioInterfaceCapabilities: ar.result is null");
+                        return;
+                    }
                     log("setupRadioInterfaceCapabilities: "
                             + "mRadioInterfaceCapabilities now setup");
                     mRadioInterfaceCapabilities =
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index c91bf40..241d510 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -581,19 +581,17 @@
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+            final String action = intent.getAction();
+            if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                 int phoneId = intent.getExtras().getInt(CarrierConfigManager.EXTRA_SLOT_INDEX);
                 // Ignore the carrier config changed if the phoneId is not matched.
                 if (phoneId == mPhone.getPhoneId()) {
                     sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
                 }
-                return;
-            }
-
-            if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
+            } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) {
                 // Update emergency string or operator name, polling service state.
                 pollState();
-            } else if (intent.getAction().equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
+            } else if (action.equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
                 String lastKnownNetworkCountry = intent.getStringExtra(
                         TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY);
                 if (!mLastKnownNetworkCountry.equals(lastKnownNetworkCountry)) {
@@ -1083,8 +1081,7 @@
     /**
      * @return the current reasons for which the radio is off.
      */
-    @VisibleForTesting
-    public Set<Integer> getRadioPowerOffReasonsForTest() {
+    public Set<Integer> getRadioPowerOffReasons() {
         return sRadioPowerOffReasons;
     }
 
@@ -1716,7 +1713,6 @@
                         log("EVENT_PHYSICAL_CHANNEL_CONFIG: size=" + list.size() + " list="
                                 + list);
                     }
-                    mPhone.notifyPhysicalChannelConfig(list);
                     mLastPhysicalChannelConfigList = list;
                     boolean hasChanged = false;
                     if (updateNrStateFromPhysicalChannelConfigs(list, mSS)) {
@@ -1730,6 +1726,7 @@
                     hasChanged |= RatRatcheter
                             .updateBandwidths(getBandwidthsFromConfigs(list), mSS);
 
+                    mPhone.notifyPhysicalChannelConfig(list);
                     // Notify NR frequency, NR connection status or bandwidths changed.
                     if (hasChanged) {
                         mPhone.notifyServiceStateChanged(mSS);
@@ -6243,8 +6240,7 @@
                 continue;
             }
             for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) {
-                if (ran == info.getRadioAccessNetworkType()
-                        && measurement == info.getSignalMeasurementType()) {
+                if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)) {
                     for (int appThreshold : info.getThresholds()) {
                         target.add(appThreshold);
                     }
@@ -6275,12 +6271,44 @@
         sendMessage(obtainMessage(EVENT_ON_DEVICE_IDLE_STATE_CHANGED, isDeviceIdle));
     }
 
+    boolean shouldEnableSignalThresholdForAppRequest(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalThresholdInfo.SignalMeasurementType int measurement,
+            int subId,
+            boolean isDeviceIdle) {
+        for (SignalRequestRecord record : mSignalRequestRecords) {
+            if (subId != record.mSubId) {
+                continue;
+            }
+            for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) {
+                if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)
+                        && (!isDeviceIdle || isSignalReportRequestedWhileIdle(record.mRequest))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static boolean isRanAndSignalMeasurementTypeMatch(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalThresholdInfo.SignalMeasurementType int measurement,
+            SignalThresholdInfo info) {
+        return ran == info.getRadioAccessNetworkType()
+                && measurement == info.getSignalMeasurementType();
+    }
+
+    private static boolean isSignalReportRequestedWhileIdle(SignalStrengthUpdateRequest request) {
+        return request.isSystemThresholdReportingRequestedWhileIdle()
+                || request.isReportingRequestedWhileIdle();
+    }
+
     private class SignalRequestRecord implements IBinder.DeathRecipient {
         final int mSubId; // subId the request originally applied to
         final int mCallingUid;
         final SignalStrengthUpdateRequest mRequest;
 
-        SignalRequestRecord(int subId, int uid, SignalStrengthUpdateRequest request) {
+        SignalRequestRecord(int subId, int uid, @NonNull SignalStrengthUpdateRequest request) {
             this.mCallingUid = uid;
             this.mSubId = subId;
             this.mRequest = request;
@@ -6295,8 +6323,7 @@
     private void updateAlwaysReportSignalStrength() {
         final int curSubId = mPhone.getSubId();
         boolean alwaysReport = mSignalRequestRecords.stream().anyMatch(
-                srr -> srr.mSubId == curSubId && (srr.mRequest.isReportingRequestedWhileIdle()
-                        || srr.mRequest.isSystemThresholdReportingRequestedWhileIdle()));
+                srr -> srr.mSubId == curSubId && isSignalReportRequestedWhileIdle(srr.mRequest));
 
         // TODO(b/177924721): TM#setAlwaysReportSignalStrength will be removed and we will not
         // worry about unset flag which was set by other client.
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java b/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
index 0ded77e..6d6d0ea 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
@@ -33,49 +33,52 @@
 /** Ef data from carrier config. */
 public final class CarrierConfigEfData implements EfData {
     private static final String TAG = "CarrierConfigEfData";
-    private final PersistableBundle mConfig;
+
+    private final String mSpn;
+    private final int mSpnDisplayCondition;
+    private final String[] mSpdi;
+    private final String[] mEhplmn;
+    private final String[] mPnn;
+    private final String[] mOpl;
 
     public CarrierConfigEfData(@NonNull PersistableBundle config) {
-        mConfig = config;
+        // Save only the relevant keys of the config.
+        mSpn = config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+        mSpnDisplayCondition = config.getInt(
+                CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT,
+                IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK);
+        mSpdi = config.getStringArray(CarrierConfigManager.KEY_SPDI_OVERRIDE_STRING_ARRAY);
+        mEhplmn = config.getStringArray(CarrierConfigManager.KEY_EHPLMN_OVERRIDE_STRING_ARRAY);
+        mPnn = config.getStringArray(CarrierConfigManager.KEY_PNN_OVERRIDE_STRING_ARRAY);
+        mOpl = config.getStringArray(CarrierConfigManager.KEY_OPL_OVERRIDE_STRING_ARRAY);
     }
 
     @Override
     public String getServiceProviderName() {
-        String spn = mConfig.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
-        if (TextUtils.isEmpty(spn)) return null;
-        return spn;
+        return TextUtils.isEmpty(mSpn) ? null : mSpn;
     }
 
     @Override
     public int getServiceProviderNameDisplayCondition(boolean isRoaming) {
-        int condition = mConfig.getInt(
-                CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT,
-                IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK);
-        return condition;
+        return mSpnDisplayCondition;
     }
 
     @Override
     public List<String> getServiceProviderDisplayInformation() {
-        String[] spdi = mConfig.getStringArray(
-                CarrierConfigManager.KEY_SPDI_OVERRIDE_STRING_ARRAY);
-        return spdi != null ? Arrays.asList(spdi) : null;
+        return mSpdi != null ? Arrays.asList(mSpdi) : null;
     }
 
     @Override
     public List<String> getEhplmnList() {
-        String[] ehplmn = mConfig.getStringArray(
-                CarrierConfigManager.KEY_EHPLMN_OVERRIDE_STRING_ARRAY);
-        return ehplmn != null ? Arrays.asList(ehplmn) : null;
+        return mEhplmn != null ? Arrays.asList(mEhplmn) : null;
     }
 
     @Override
     public List<PlmnNetworkName> getPlmnNetworkNameList() {
-        String[] pnn = mConfig.getStringArray(
-                CarrierConfigManager.KEY_PNN_OVERRIDE_STRING_ARRAY);
         List<PlmnNetworkName> pnnList = null;
-        if (pnn != null) {
-            pnnList = new ArrayList<>(pnn.length);
-            for (String pnnStr : pnn) {
+        if (mPnn != null) {
+            pnnList = new ArrayList<>(mPnn.length);
+            for (String pnnStr : mPnn) {
                 try {
                     String[] names = pnnStr.split("\\s*,\\s*");
                     String alphal = names[0];
@@ -91,13 +94,10 @@
 
     @Override
     public List<OperatorPlmnInfo> getOperatorPlmnList() {
-        // OPL
-        String[] opl = mConfig.getStringArray(
-                CarrierConfigManager.KEY_OPL_OVERRIDE_STRING_ARRAY);
         List<OperatorPlmnInfo> oplList = null;
-        if (opl != null) {
-            oplList = new ArrayList<>(opl.length);
-            for (String oplStr : opl) {
+        if (mOpl != null) {
+            oplList = new ArrayList<>(mOpl.length);
+            for (String oplStr : mOpl) {
                 try {
                     String[] info = oplStr.split("\\s*,\\s*");
                     oplList.add(new OperatorPlmnInfo(
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 7b1edd7..0551086 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -529,7 +529,10 @@
         return mCid;
     }
 
-    ApnSetting getApnSetting() {
+    /**
+     * @return DataConnection's ApnSetting.
+     */
+    public ApnSetting getApnSetting() {
         return mApnSetting;
     }
 
@@ -2421,6 +2424,7 @@
                     mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
                     mApnSetting != null
                         ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+            mDataCallSessionStats.onDataCallDisconnected(mDcFailCause);
             if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) {
                 // This is from source data connection to set itself's state
                 setHandoverState(HANDOVER_STATE_COMPLETED);
@@ -2916,7 +2920,6 @@
 
             TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
                     mCid, mApnSetting.getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
-            mDataCallSessionStats.onDataCallDisconnected();
 
             mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener);
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index d2fa226..4d4ca81 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -1593,8 +1593,7 @@
             }
 
             // Check if it fails because of the existing data is still disconnecting.
-            if (dataConnectionReasons.containsOnly(
-                    DataDisallowedReasonType.DATA_IS_DISCONNECTING)
+            if (dataConnectionReasons.contains(DataDisallowedReasonType.DATA_IS_DISCONNECTING)
                     && isHandoverPending(apnContext.getApnTypeBitmask())) {
                 // Normally we don't retry when isDataAllow() returns false, because that's consider
                 // pre-condition not met, for example, data not enabled by the user, or airplane
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index 4081cd6..df3f0f1 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -130,7 +130,15 @@
         return makeNetworkFilter(subscriptionId);
     }
 
-    private NetworkCapabilities makeNetworkFilter(int subscriptionId) {
+    /**
+     * Build the network request filter used by this factory.
+     * @param subscriptionId the subscription ID to listen to
+     * @return the filter to send to the system server
+     */
+    // This is used by the test to simulate the behavior of the system server, which is to
+    // send requests that match the network filter.
+    @VisibleForTesting
+    public NetworkCapabilities makeNetworkFilter(int subscriptionId) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index 3b4fc69..da350c3 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -22,8 +22,10 @@
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
 
+import android.annotation.Nullable;
 import android.os.SystemClock;
 import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.DataFailCause;
 import android.telephony.ServiceState;
@@ -50,8 +52,7 @@
 
     private final Phone mPhone;
     private long mStartTime;
-    private boolean mOnRatChangedCalledBeforeSetup = false;
-    DataCallSession mOngoingDataCall;
+    @Nullable private DataCallSession mDataCallSession;
 
     private final PersistAtomsStorage mAtomsStorage =
             PhoneFactory.getMetricsCollector().getAtomsStorage();
@@ -64,22 +65,8 @@
 
     /** Creates a new ongoing atom when data call is set up. */
     public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
-        if (!mOnRatChangedCalledBeforeSetup) {
-            // there shouldn't be an ongoing data call here, if that's the case, it means that
-            // deactivateDataCall hasn't been processed properly, so we save the previous atom here
-            // and move on to create a new atom.
-            if (mOngoingDataCall != null) {
-                mOngoingDataCall.failureCause = DataFailCause.UNKNOWN;
-                onDataCallDisconnected();
-            }
-            mOngoingDataCall = getDefaultProto(apnTypeBitMask);
-            mStartTime = getTimeMillis();
-        } else {
-            // if onRatChanged was called before onSetupDataCall, the atom is already initialized
-            // but apnTypeBitMask is initialized to 0, so we need to update it
-            mOngoingDataCall.apnTypeBitmask = apnTypeBitMask;
-        }
-        mOnRatChangedCalledBeforeSetup = false;
+        mDataCallSession = getDefaultProto(apnTypeBitMask);
+        mStartTime = getTimeMillis();
     }
 
     /**
@@ -92,89 +79,90 @@
      * @param failureCause failure cause as per android.telephony.DataFailCause
      */
     public synchronized void onSetupDataCallResponse(
-            DataCallResponse response,
+            @Nullable DataCallResponse response,
             @RilRadioTechnology int radioTechnology,
             @ApnType int apnTypeBitmask,
             @ProtocolType int protocol,
-            int failureCause) {
-        // there should've been another call to initiate the atom,
+            @DataFailureCause int failureCause) {
+        // there should've been a call to onSetupDataCall to initiate the atom,
         // so this method is being called out of order -> no metric will be logged
-        if (mOngoingDataCall == null) {
+        if (mDataCallSession == null) {
             loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
             return;
         }
-        mOngoingDataCall.ratAtEnd = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+        mDataCallSession.ratAtEnd = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
 
         // only set if apn hasn't been set during setup
-        if (mOngoingDataCall.apnTypeBitmask == 0) {
-            mOngoingDataCall.apnTypeBitmask = apnTypeBitmask;
+        if (mDataCallSession.apnTypeBitmask == 0) {
+            mDataCallSession.apnTypeBitmask = apnTypeBitmask;
         }
 
-        mOngoingDataCall.ipType = protocol;
-        mOngoingDataCall.failureCause = failureCause;
+        mDataCallSession.ipType = protocol;
+        mDataCallSession.failureCause = failureCause;
         if (response != null) {
-            mOngoingDataCall.suggestedRetryMillis =
+            mDataCallSession.suggestedRetryMillis =
                     (int) Math.min(response.getRetryDurationMillis(), Integer.MAX_VALUE);
+            // If setup has failed, then store the atom
             if (failureCause != DataFailCause.NONE) {
-                mOngoingDataCall.failureCause = failureCause;
-                mOngoingDataCall.setupFailed = true;
-                // set dataCall as inactive
-                mOngoingDataCall.ongoing = false;
-                // store it only if setup has failed
-                mAtomsStorage.addDataCallSession(mOngoingDataCall);
-                mOngoingDataCall = null;
+                mDataCallSession.failureCause = failureCause;
+                mDataCallSession.setupFailed = true;
+                mDataCallSession.ongoing = false;
+                mAtomsStorage.addDataCallSession(mDataCallSession);
+                mDataCallSession = null;
             }
         }
     }
 
     /**
-     * Updates the ongoing dataCall's atom when data call is deactivated.
+     * Updates the dataCall atom when data call is deactivated.
      *
      * @param reason Deactivate reason
      */
     public synchronized void setDeactivateDataCallReason(@DeactivateDataReason int reason) {
         // there should've been another call to initiate the atom,
         // so this method is being called out of order -> no metric will be logged
-        if (mOngoingDataCall == null) {
-            loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+        if (mDataCallSession == null) {
+            loge("setDeactivateDataCallReason: no DataCallSession atom has been initiated.");
             return;
         }
         switch (reason) {
             case DataService.REQUEST_REASON_NORMAL:
-                mOngoingDataCall.deactivateReason =
+                mDataCallSession.deactivateReason =
                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
                 break;
             case DataService.REQUEST_REASON_SHUTDOWN:
-                mOngoingDataCall.deactivateReason =
+                mDataCallSession.deactivateReason =
                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
                 break;
             case DataService.REQUEST_REASON_HANDOVER:
-                mOngoingDataCall.deactivateReason =
+                mDataCallSession.deactivateReason =
                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
                 break;
             default:
-                mOngoingDataCall.deactivateReason =
+                mDataCallSession.deactivateReason =
                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
                 break;
         }
-
-        mOngoingDataCall.oosAtEnd = getIsOos();
     }
 
-    /** Stores the atom when DataConnection reaches DISCONNECTED state. */
-    public synchronized void onDataCallDisconnected() {
+    /** Stores the atom when DataConnection reaches DISCONNECTED state.
+     *  @param failureCause failure cause as per android.telephony.DataFailCause
+     **/
+    public synchronized void onDataCallDisconnected(@DataFailureCause int failureCause) {
         // there should've been another call to initiate the atom,
         // so this method is being called out of order -> no atom will be saved
-        if (mOngoingDataCall == null) {
-            loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+        if (mDataCallSession == null) {
+            loge("onDataCallDisconnected: no DataCallSession atom has been initiated.");
             return;
         }
-        mOngoingDataCall.ongoing = false;
-        mOngoingDataCall.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime);
+        mDataCallSession.failureCause = failureCause;
+        mDataCallSession.oosAtEnd = getIsOos();
+        mDataCallSession.ongoing = false;
+        mDataCallSession.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime);
         // store for the data call list event, after DataCall is disconnected and entered into
         // inactive mode
-        mAtomsStorage.addDataCallSession(mOngoingDataCall);
-        mOngoingDataCall = null;
+        mAtomsStorage.addDataCallSession(mDataCallSession);
+        mDataCallSession = null;
     }
 
     /**
@@ -184,24 +172,19 @@
      * registration state change.
      */
     public synchronized void onDrsOrRatChanged(@RilRadioTechnology int radioTechnology) {
-        @NetworkType int rat = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
-        // if no data call is initiated, or we have a new data call while the last one has ended
-        // because onRatChanged might be called before onSetupDataCall
-        if (mOngoingDataCall == null) {
-            mOngoingDataCall = getDefaultProto(0);
-            mOngoingDataCall.ratAtEnd = rat;
-            mStartTime = getTimeMillis();
-            mOnRatChangedCalledBeforeSetup = true;
+        @NetworkType int currentRat =
+                ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+        if (mDataCallSession != null
+                && currentRat != TelephonyManager.NETWORK_TYPE_UNKNOWN
+                && mDataCallSession.ratAtEnd != currentRat) {
+            mDataCallSession.ratSwitchCount++;
+            mDataCallSession.ratAtEnd = currentRat;
+            mDataCallSession.bandAtEnd = ServiceStateStats.getBand(mPhone, currentRat);
         }
-        if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN && mOngoingDataCall.ratAtEnd != rat) {
-            mOngoingDataCall.ratSwitchCount++;
-            mOngoingDataCall.ratAtEnd = rat;
-        }
-        mOngoingDataCall.bandAtEnd = ServiceStateStats.getBand(mPhone, rat);
     }
 
     private static long convertMillisToMinutes(long millis) {
-        return Math.round(millis / 60000);
+        return Math.round(millis / 60000.0);
     }
 
     /** Creates a proto for a normal {@code DataCallSession} with default values. */
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
index df20401..b348c61 100644
--- a/src/java/com/android/internal/telephony/uicc/PinStorage.java
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -25,6 +25,9 @@
 
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__CACHED_PIN_DISCARDED;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_COUNT_NOT_MATCHING_AFTER_REBOOT;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_DECRYPTION_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_ERROR;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_REQUIRED_AFTER_REBOOT;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_STORED_FOR_VERIFICATION;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
@@ -125,6 +128,7 @@
     private static final String SHARED_PREFS_NAME = "pinstorage_prefs";
     private static final String SHARED_PREFS_AVAILABLE_PIN_BASE_KEY = "encrypted_pin_available_";
     private static final String SHARED_PREFS_REBOOT_PIN_BASE_KEY = "encrypted_pin_reboot_";
+    private static final String SHARED_PREFS_STORED_PINS = "stored_pins";
 
     // Events
     private static final int ICC_CHANGED_EVENT = 1;
@@ -347,6 +351,9 @@
                     PIN_STORAGE_EVENT__EVENT__PIN_REQUIRED_AFTER_REBOOT, notAvailableCount);
         }
 
+        // Save number of PINs to generate metrics after reboot
+        saveNumberOfCachedPins(storedCount);
+
         return result;
     }
 
@@ -407,7 +414,7 @@
         mShortTermSecretKey =
                 initializeSecretKey(KEYSTORE_ALIAS_SHORT_TERM, /*createIfAbsent=*/ false);
 
-        boolean otaReboot = false;
+        int verificationReadyCount = 0;
         int slotCount = getSlotCount();
         for (int slotId = 0; slotId < slotCount; slotId++) {
             // Read PIN information from storage
@@ -434,12 +441,22 @@
             if (storedPin.status == PinStatus.REBOOT_READY) {
                 storedPin.status = PinStatus.VERIFICATION_READY;
                 savePinInformation(slotId, storedPin);
-                otaReboot = true;
+                verificationReadyCount++;
             }
         }
-        if (otaReboot) {
+        if (verificationReadyCount > 0) {
             startTimer(TIMER_VALUE_AFTER_OTA_MILLIS);
         }
+
+        // Generate metrics for PINs that had been stored before reboot, but are not available
+        // after. This can happen if there is an excessive delay in unlocking the device (short
+        // term key expires), but also if a new SIM card without PIN is present.
+        int prevCachedPinCount = saveNumberOfCachedPins(0);
+        if (prevCachedPinCount > verificationReadyCount) {
+            TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+                    PIN_STORAGE_EVENT__EVENT__PIN_COUNT_NOT_MATCHING_AFTER_REBOOT,
+                    prevCachedPinCount - verificationReadyCount);
+        }
     }
 
     /**
@@ -484,6 +501,9 @@
         deleteSecretKey(KEYSTORE_ALIAS_SHORT_TERM);
         mShortTermSecretKey = null;
 
+        // Reset number of stored PINs (applicable if timer expired before unattended reboot).
+        saveNumberOfCachedPins(0);
+
         // Write metrics about number of discarded PINs
         if (discardedPin > 0) {
             TelephonyStatsLog.write(PIN_STORAGE_EVENT,
@@ -892,6 +912,19 @@
         }
     }
 
+    /**
+     * Saves the number of cached PINs ready for verification after reboot and returns the
+     * previous value.
+     */
+    private int saveNumberOfCachedPins(int storedCount) {
+        SharedPreferences sharedPrefs =
+                mContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+
+        int previousValue = sharedPrefs.getInt(SHARED_PREFS_STORED_PINS, 0);
+        sharedPrefs.edit().putInt(SHARED_PREFS_STORED_PINS, storedCount).commit();
+        return previousValue;
+    }
+
     private boolean startTimer(int duration) {
         removeMessages(TIMER_EXPIRATION_EVENT);
         return duration > 0 ? sendEmptyMessageDelayed(TIMER_EXPIRATION_EVENT, duration) : true;
@@ -1118,6 +1151,8 @@
             return EncryptedPin.toByteArray(encryptedPin);
         } catch (Exception e) {
             loge("Encrypt exception", e);
+            TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+                    PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_ERROR, 1);
         }
         return new byte[0];
     }
@@ -1141,6 +1176,8 @@
             }
         } catch (Exception e) {
             loge("Decrypt exception", e);
+            TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+                    PIN_STORAGE_EVENT__EVENT__PIN_DECRYPTION_ERROR, 1);
         }
         return new byte[0];
     }
diff --git a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
index 280176f..a81e93f 100644
--- a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
@@ -277,14 +277,17 @@
     @Test
     @SmallTest
     public void testIsRcsVolteSingleRegistrationSupported() {
-        String[] vals = new String[]{"0", "1", "false", "true"};
-        boolean[] expectedRes = new boolean[]{false, true, false, true};
+        String[] vals = new String[]{"0", "1", "2"};
+        boolean[] expectedResHome = new boolean[]{false, true, true};
+        boolean[] expectedResRoaming = new boolean[]{false, true, false};
         for (int i = 0; i < vals.length; i++) {
             String xml = "\t\t\t\t<characteristic type=\"GSMA\">\n"
                     + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\""
                     + vals[i] + "\"/>\n" + "\t\t\t\t</characteristic>\n";
             RcsConfig config = new RcsConfig(xml.getBytes());
-            assertEquals(config.isRcsVolteSingleRegistrationSupported(), expectedRes[i]);
+            assertEquals(config.isRcsVolteSingleRegistrationSupported(false), expectedResHome[i]);
+            assertEquals(config.isRcsVolteSingleRegistrationSupported(true),
+                    expectedResRoaming[i]);
         }
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index c93de7d..9058a44 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -50,6 +50,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
@@ -62,6 +63,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
@@ -132,6 +134,8 @@
         doReturn(true).when(mSubControllerMock).isOpportunistic(5);
         doReturn(1).when(mPhoneMock1).getSubId();
         doReturn(2).when(mPhoneMock2).getSubId();
+        mPhoneMock1.mCi = mSimulatedCommands;
+        mPhoneMock2.mCi = mSimulatedCommands;
         List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo2);
         doReturn(infoList).when(mSubControllerMock)
                 .getActiveSubscriptionInfoList(anyString(), nullable(String.class));
@@ -163,7 +167,7 @@
 
     @Test
     @SmallTest
-    public void testTestSubInfoChangeBeforeAllSubReady() throws Exception {
+    public void testSubInfoChangeBeforeAllSubReady() throws Exception {
         doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
                 .getDefaultDataSubId();
         doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
@@ -199,6 +203,50 @@
     }
 
     @Test
+    public void testSubInfoChangeAfterRadioUnavailable() throws Exception {
+        mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+        mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+        mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+        processAllMessages();
+
+        // Notify radio unavailable.
+        replaceInstance(BaseCommands.class, "mState", mSimulatedCommands,
+                TelephonyManager.RADIO_POWER_UNAVAILABLE);
+        mMultiSimSettingControllerUT.obtainMessage(
+                MultiSimSettingController.EVENT_RADIO_STATE_CHANGED).sendToTarget();
+
+        // Mark all subs as inactive.
+        doReturn(false).when(mSubControllerMock).isActiveSubId(1);
+        doReturn(false).when(mSubControllerMock).isActiveSubId(2);
+        doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
+        doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
+        doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock1).getSubId();
+        doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
+        List<SubscriptionInfo> infoList = new ArrayList<>();
+        doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
+                nullable(String.class));
+        doReturn(new int[]{}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+        clearInvocations(mSubControllerMock);
+
+        // The below sub info change should be ignored.
+        mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
+        processAllMessages();
+        verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+        verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
+        verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt());
+
+        // Send all sub ready notification
+        mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+        processAllMessages();
+
+        // Everything should be set to invalid since nothing is active.
+        verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        verify(mSubControllerMock)
+                .setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
+    @Test
     @SmallTest
     public void testSingleActiveDsds() throws Exception {
         doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index ab25842..1fc5d37 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -29,14 +29,17 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PcoData;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.dataconnection.DcController;
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
@@ -45,6 +48,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -66,9 +70,14 @@
     private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
     private static final int EVENT_INITIALIZE = 12;
     private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
+    private static final int EVENT_PCO_DATA_CHANGED = 14;
 
     private NetworkTypeController mNetworkTypeController;
     private PersistableBundle mBundle;
+    @Mock
+    DataConnection mDataConnection;
+    @Mock
+    ApnSetting mApnSetting;
 
     private IState getCurrentState() throws Exception {
         Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
@@ -103,6 +112,8 @@
         doReturn(RadioAccessFamily.getRafFromNetworkType(
                 TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA)).when(
                 mPhone).getCachedAllowedNetworkTypesBitmask();
+        doReturn(false).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
         mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
         processAllMessages();
     }
@@ -275,6 +286,22 @@
     }
 
     @Test
+    public void testTransitionToCurrentStateIdleSupportPhysicalChannelConfig1_6() throws Exception {
+        doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+        mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+        processAllMessages();
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+        setPhysicalLinkState(false);
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+    }
+
+    @Test
     public void testTransitionToCurrentStateLteConnected() throws Exception {
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -287,6 +314,23 @@
     }
 
     @Test
+    public void testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6()
+            throws Exception {
+        doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+        mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+        processAllMessages();
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+        setPhysicalLinkState(true);
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+    }
+
+    @Test
     public void testTransitionToCurrentStateNrConnected() throws Exception {
         assertEquals("DefaultState", getCurrentState().getName());
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -322,9 +366,9 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setBand(41)
                 .build();
-        List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = new ArrayList<>();
-        mLastPhysicalChannelConfigList.add(physicalChannelConfig);
-        doReturn(mLastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+        List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+        lastPhysicalChannelConfigList.add(physicalChannelConfig);
+        doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
         broadcastCarrierConfigs();
 
         mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
@@ -345,9 +389,9 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setBand(2)
                 .build();
-        List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = new ArrayList<>();
-        mLastPhysicalChannelConfigList.add(physicalChannelConfig);
-        doReturn(mLastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+        List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+        lastPhysicalChannelConfigList.add(physicalChannelConfig);
+        doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
         broadcastCarrierConfigs();
 
         mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
@@ -356,6 +400,92 @@
     }
 
     @Test
+    public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapable() throws Exception {
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
+        broadcastCarrierConfigs();
+
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("connected_mmwave", getCurrentState().getName());
+    }
+
+    @Test
+    public void testTransitionToCurrentStateNrConnectedWithPcoAndNoNrAdvancedCapable()
+            throws Exception {
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+        broadcastCarrierConfigs();
+        int cid = 1;
+        byte[] contents = new byte[]{0};
+        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+        broadcastCarrierConfigs();
+
+
+        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("connected", getCurrentState().getName());
+    }
+
+    @Test
+    public void testTransitionToCurrentStateNrConnectedWithWrongPcoAndNoNrAdvancedCapable()
+            throws Exception {
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+        broadcastCarrierConfigs();
+        int cid = 1;
+        byte[] contents = new byte[]{1};
+        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF00);
+        broadcastCarrierConfigs();
+
+
+        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("connected", getCurrentState().getName());
+    }
+
+    @Test
+    public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapableAndPco()
+            throws Exception {
+        assertEquals("DefaultState", getCurrentState().getName());
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+        int cid = 1;
+        byte[] contents = new byte[]{1};
+        doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+        doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+        doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+        mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+        broadcastCarrierConfigs();
+
+        mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+                new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("connected_mmwave", getCurrentState().getName());
+    }
+
+    @Test
     public void testEventDataRatChanged() throws Exception {
         testTransitionToCurrentStateLegacy();
         doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -415,12 +545,126 @@
     }
 
     @Test
+    public void testNrPhysicalChannelChange1_6FromNrConnectedMmwaveToLteConnected()
+            throws Exception {
+        doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+        mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+        processAllMessages();
+        testTransitionToCurrentStateNrConnectedMmwave();
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+        setPhysicalLinkState(true);
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+        mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+
+        processAllMessages();
+
+        assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+    }
+
+    @Test
+    public void testEventPhysicalChannelChangeFromLteToLteCaInLegacyState() throws Exception {
+        testTransitionToCurrentStateLegacy();
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        updateOverrideNetworkType();
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+        doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        processAllMessages();
+
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
+    public void testEventPhysicalChannelChangeFromLteToLteCaInLteConnectedState() throws Exception {
+        // Remove RRC idle/RRC connected from 5G override
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
+                "connected_mmwave:5G_Plus,connected:5G");
+        broadcastCarrierConfigs();
+
+        // Transition to LTE connected state
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+                new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null));
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // LTE -> LTE+
+        doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+        doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        processAllMessages();
+
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
+    public void testEventPhysicalChannelChangeFromLteToLteCaInIdleState() throws Exception {
+        // Remove RRC idle/RRC connected from 5G override
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
+                "connected_mmwave:5G_Plus,connected:5G");
+        broadcastCarrierConfigs();
+
+        // Transition to idle state
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+                new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+        mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+        processAllMessages();
+        assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // LTE -> LTE+
+        doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+        doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        processAllMessages();
+
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
     public void testEventPhysicalLinkStateChanged() throws Exception {
         testTransitionToCurrentStateLteConnected();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
         mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
                 new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+
         processAllMessages();
+
+        assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+    }
+
+    @Test
+    public void testEventPhysicalLinkStateChangedSupportPhysicalChannelConfig1_6()
+            throws Exception {
+        doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+        mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+        processAllMessages();
+        testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
+        doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+        setPhysicalLinkState(false);
+        mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+
+        processAllMessages();
+
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
     }
 
@@ -837,4 +1081,21 @@
                 mNetworkTypeController.getOverrideNetworkType());
         assertFalse(mNetworkTypeController.is5GHysteresisActive());
     }
+
+    private void setPhysicalLinkState(Boolean state) {
+        List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+        // If PhysicalChannelConfigList is empty, PhysicalLinkState is DcController
+        // .PHYSICAL_LINK_NOT_ACTIVE
+        // If PhysicalChannelConfigList is not empty, PhysicalLinkState is DcController
+        // .PHYSICAL_LINK_ACTIVE
+
+        if (state) {
+            PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
+                    .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+                    .setBand(41)
+                    .build();
+            lastPhysicalChannelConfigList.add(physicalChannelConfig);
+        }
+        doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 067454f..54ac7a3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -269,11 +269,6 @@
         waitUntilReady();
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
 
-        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
-        mContext.sendBroadcast(intent);
-        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
-
         // Override SPN related resource
         mContextFixture.putResource(
                 com.android.internal.R.string.lockscreen_carrier_default,
@@ -334,6 +329,12 @@
                     15, /* SIGNAL_STRENGTH_GOOD */
                     30  /* SIGNAL_STRENGTH_GREAT */
                 });
+
+        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
+        mContext.sendBroadcast(intent);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
         logd("ServiceStateTrackerTest -Setup!");
     }
 
@@ -404,33 +405,33 @@
     public void testSetRadioPowerForReason() {
         // Radio does not turn on if off for other reason and not emergency call.
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().isEmpty());
+        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
         sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().contains(Phone.RADIO_POWER_REASON_THERMAL));
-        assertTrue(sst.getRadioPowerOffReasonsForTest().size() == 1);
+        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
         sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_USER);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().contains(Phone.RADIO_POWER_REASON_THERMAL));
-        assertTrue(sst.getRadioPowerOffReasonsForTest().size() == 1);
+        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
 
         // Radio power state reason is removed and radio turns on if turned on for same reason it
         // had been turned off for.
         sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().isEmpty());
+        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
 
         // Turn radio off, then successfully turn radio on for emergency call.
         sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().contains(Phone.RADIO_POWER_REASON_THERMAL));
-        assertTrue(sst.getRadioPowerOffReasonsForTest().size() == 1);
+        assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+        assertTrue(sst.getRadioPowerOffReasons().size() == 1);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
         sst.setRadioPower(true, true, true, false);
-        assertTrue(sst.getRadioPowerOffReasonsForTest().isEmpty());
+        assertTrue(sst.getRadioPowerOffReasons().isEmpty());
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
     }
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 ce5dc21..c0b8d9a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -2829,6 +2829,7 @@
         doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
         doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
         doReturn(false).when(mApnContext).isConnectable();
+        doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
         doReturn(DctConstants.State.DISCONNECTING).when(mApnContext).getState();
         replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
         replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
@@ -2850,6 +2851,7 @@
         doReturn(DctConstants.State.RETRYING).when(mApnContext).getState();
         // Data now is disconnected
         doReturn(true).when(mApnContext).isConnectable();
+        doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
                 new AsyncResult(Pair.create(mApnContext, 0), null, null)));
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index 0aa2a4d..7dd65c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.AsyncResult;
@@ -42,6 +43,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArraySet;
 
 import androidx.test.filters.FlakyTest;
 
@@ -56,6 +58,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 
@@ -77,6 +80,9 @@
 
     private String mTestName = "";
 
+    // List of all requests filed by a test
+    private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
+    // List of requests active in DcTracker
     private final ArrayList<NetworkRequest> mNetworkRequestList = new ArrayList<>();
 
     private TelephonyNetworkFactory mTelephonyNetworkFactoryUT;
@@ -155,7 +161,9 @@
                         "mobile_ia,14,0,2,-1,true", "mobile_emergency,15,0,2,-1,true"});
 
         doAnswer(invocation -> {
-            mNetworkRequestList.add((NetworkRequest) invocation.getArguments()[0]);
+            final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0];
+            mNetworkRequestList.add(req);
+            mAllNetworkRequestSet.add(req);
             return null;
         }).when(mDcTracker).requestNetwork(any(), anyInt(), any());
 
@@ -174,8 +182,36 @@
         replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mPhoneSwitcher);
 
         mTelephonyNetworkFactoryUT = new TelephonyNetworkFactory(Looper.myLooper(), mPhone);
-        verify(mConnectivityManager).registerNetworkProvider(any());
+        final ArgumentCaptor<NetworkProvider> providerCaptor =
+                ArgumentCaptor.forClass(NetworkProvider.class);
+        verify(mConnectivityManager).registerNetworkProvider(providerCaptor.capture());
+        // For NetworkFactory to function as expected, the provider ID must be set to some
+        // number > 0.
+        providerCaptor.getValue().setProviderId(1);
         verify(mPhoneSwitcher).registerForActivePhoneSwitch(any(), anyInt(), any());
+
+        // Simulate the behavior of the system server. When offerNetwork is called, it will
+        // update the factory about all requests that pass the registered filter, by calling
+        // NetworkProvider#onNetworkNeeded or onNetworkUnneeded.
+        // Note that this simulation is a little bit incomplete, as the system server will
+        // *update* only for those requests for which the status has changed, but this
+        // simulation will send REQUEST_NETWORK or CANCEL_REQUEST for all registered requests.
+        // At this time it makes no difference in this test.
+        // Also, this test reads from mAllNetworkRequestSet, which is not really the list of
+        // requests sent to the system server as the test doesn't instrument that. Instead, it's
+        // the list of requests ever sent to the factory. This also makes no difference in this
+        // test at this time.
+        doAnswer(invocation -> {
+            final NetworkCapabilities capabilitiesFilter =
+                    mTelephonyNetworkFactoryUT.makeNetworkFilter(
+                            mSubscriptionController.getSubIdUsingPhoneId(0));
+            for (final NetworkRequest request : mAllNetworkRequestSet) {
+                final int message = request.canBeSatisfiedBy(capabilitiesFilter)
+                        ? CMD_REQUEST_NETWORK : CMD_CANCEL_REQUEST;
+                mTelephonyNetworkFactoryUT.obtainMessage(message, 0, 0, request).sendToTarget();
+            }
+            return null;
+        }).when(mConnectivityManager).offerNetwork(anyInt(), any(), any(), any());
     }
 
     /**