Merge "Enable SignalThreshold with consideration of both system and apps"
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/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 2155c4a..8fea92f 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
@@ -1844,6 +1845,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/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 0699d49..d036721 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -1083,8 +1083,7 @@
     /**
      * @return the current reasons for which the radio is off.
      */
-    @VisibleForTesting
-    public Set<Integer> getRadioPowerOffReasonsForTest() {
+    public Set<Integer> getRadioPowerOffReasons() {
         return sRadioPowerOffReasons;
     }
 
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/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/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/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)));