Add a subscription record in simTable for inactive SIM.

We need a subId if we want to enable a SIM in an inactive
SIM slot.
Bug: 149359276
Test: unittest and manual - add a new pSIM in single SIM
device with eSIM active. Verify that pSIM record is added
and it shows up in Settings page.

Change-Id: Ifb9e5594d1abcd0b8270cfe9ec0c5260e8d86eb1
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 44bfcc9..4a17582 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -1493,9 +1493,10 @@
         value.put(SubscriptionManager.CARRIER_NAME, "");
         value.put(SubscriptionManager.CARD_ID, uniqueId);
         value.put(SubscriptionManager.SUBSCRIPTION_TYPE, subscriptionType);
-        if (isSubscriptionForRemoteSim(subscriptionType)) {
+        if (!TextUtils.isEmpty(displayName)) {
             value.put(SubscriptionManager.DISPLAY_NAME, displayName);
-        } else {
+        }
+        if (!isSubscriptionForRemoteSim(subscriptionType)) {
             UiccCard card = mUiccController.getUiccCardForPhone(slotIndex);
             if (card != null) {
                 String cardId = card.getCardId();
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index fe600d6..a978c9a 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -91,6 +91,7 @@
     private static final int EVENT_SIM_IMSI = 11;
     private static final int EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS = 12;
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 13;
+    private static final int EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED = 14;
 
     private static final String ICCID_STRING_FOR_NO_SIM = "";
 
@@ -176,16 +177,26 @@
     /**
      * Update subscriptions when given a new ICC state.
      */
-    public void updateInternalIccState(String simStatus, String reason, int phoneId,
-            boolean absentAndInactive) {
+    public void updateInternalIccState(String simStatus, String reason, int phoneId) {
         logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason
                 + " phoneId " + phoneId);
         int message = internalIccStateToMessage(simStatus);
         if (message != EVENT_INVALID) {
-            sendMessage(obtainMessage(message, phoneId, absentAndInactive ? 1 : 0, reason));
+            sendMessage(obtainMessage(message, phoneId, 0, reason));
         }
     }
 
+    /**
+     * Update subscriptions if needed when there's a change in inactive slot.
+     * @param prevActivePhoneId is the corresponding phoneId of the slot if slot was previously
+     *                          active. It could be INVALID if it was already inactive.
+     * @param iccId iccId in that slot, if any.
+     */
+    public void updateInternalIccStateForInactiveSlot(int prevActivePhoneId, String iccId) {
+        sendMessage(obtainMessage(EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED, prevActivePhoneId,
+                0, iccId));
+    }
+
     private int internalIccStateToMessage(String simStatus) {
         switch(simStatus) {
             case IccCardConstants.INTENT_VALUE_ICC_ABSENT: return EVENT_SIM_ABSENT;
@@ -246,7 +257,11 @@
                 break;
 
             case EVENT_SIM_ABSENT:
-                handleSimAbsent(msg.arg1, msg.arg2);
+                handleSimAbsent(msg.arg1);
+                break;
+
+            case EVENT_INACTIVE_SLOT_ICC_STATE_CHANGED:
+                handleInactiveSlotIccStateChange(msg.arg1, (String) msg.obj);
                 break;
 
             case EVENT_SIM_LOCKED:
@@ -582,10 +597,31 @@
         }
     }
 
-    private void handleSimAbsent(int phoneId, int absentAndInactive) {
-        if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
-            logd("SIM" + (phoneId + 1) + " hot plug out, absentAndInactive=" + absentAndInactive);
+    /**
+     * PhoneId is the corresponding phoneId of the slot if slot was previously active.
+     * It could be INVALID if it was already inactive.
+     */
+    private void handleInactiveSlotIccStateChange(int phoneId, String iccId) {
+        // If phoneId is valid, it means the physical slot was active in that phoneId. In this case,
+        // we clear (mark inactive) the subscription in db on that phone.
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+                logd("Slot of SIM" + (phoneId + 1) + " becomes inactive");
+            }
+            cleanSubscriptionInPhone(phoneId);
         }
+        if (!TextUtils.isEmpty(iccId)) {
+            // If iccId is new, add a subscription record in the db.
+            String strippedIccId = IccUtils.stripTrailingFs(iccId);
+            if (SubscriptionController.getInstance().getSubInfoForIccId(strippedIccId) == null) {
+                SubscriptionController.getInstance().insertEmptySubInfoRecord(
+                        strippedIccId, "CARD", SubscriptionManager.INVALID_PHONE_INDEX,
+                        SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+            }
+        }
+    }
+
+    private void cleanSubscriptionInPhone(int phoneId) {
         sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
         int[] subIds = SubscriptionController.getInstance().getSubId(phoneId);
         if (subIds != null && subIds.length > 0) {
@@ -597,15 +633,23 @@
                     SubscriptionController.getSelectionForSubIdList(subIds), null);
         }
         updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
-        // Do not broadcast if the SIM is absent and inactive, because the logical phoneId here is
-        // no longer correct
-        if (absentAndInactive == 0) {
-            broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
-            broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
-            broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_UNKNOWN);
-            updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-            updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+    }
+
+    private void handleSimAbsent(int phoneId) {
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            logd("handleSimAbsent on invalid phoneId");
+            return;
         }
+        if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+            logd("SIM" + (phoneId + 1) + " hot plug out");
+        }
+        cleanSubscriptionInPhone(phoneId);
+
+        broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
+        broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
+        broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_UNKNOWN);
+        updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
     }
 
     private void handleSimError(int phoneId) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index cd59849..0de6a53 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -36,6 +36,7 @@
 import android.preference.PreferenceManager;
 import android.sysprop.TelephonyProperties;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.UiccCardInfo;
 import android.text.TextUtils;
@@ -637,23 +638,33 @@
         }
     }
 
-    static void updateInternalIccState(Context context, IccCardConstants.State state, String reason,
-            int phoneId) {
-        updateInternalIccState(context, state, reason, phoneId, false);
+    static void updateInternalIccStateForInactiveSlot(
+            Context context, int prevActivePhoneId, String iccId) {
+        if (SubscriptionManager.isValidPhoneId(prevActivePhoneId)) {
+            // Mark SIM state as ABSENT on previously phoneId.
+            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            telephonyManager.setSimStateForPhone(prevActivePhoneId,
+                    IccCardConstants.State.ABSENT.toString());
+        }
+
+        SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
+        if (subInfoUpdator != null) {
+            subInfoUpdator.updateInternalIccStateForInactiveSlot(prevActivePhoneId, iccId);
+        } else {
+            Rlog.e(LOG_TAG, "subInfoUpdate is null.");
+        }
     }
 
-    // absentAndInactive is a special case when we need to update subscriptions but don't want to
-    // broadcast a state change
     static void updateInternalIccState(Context context, IccCardConstants.State state, String reason,
-            int phoneId, boolean absentAndInactive) {
+            int phoneId) {
         TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
                 Context.TELEPHONY_SERVICE);
         telephonyManager.setSimStateForPhone(phoneId, state.toString());
 
         SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
         if (subInfoUpdator != null) {
-            subInfoUpdator.updateInternalIccState(getIccStateIntentString(state),
-                    reason, phoneId, absentAndInactive);
+            subInfoUpdator.updateInternalIccState(getIccStateIntentString(state), reason, phoneId);
         } else {
             Rlog.e(LOG_TAG, "subInfoUpdate is null.");
         }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index db0aeb7..24e5a31 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -154,12 +154,10 @@
             if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) {
                 // TODO: (b/79432584) evaluate whether should broadcast card state change
                 // even if it's inactive.
+                UiccController.updateInternalIccStateForInactiveSlot(mContext, mPhoneId, mIccId);
                 if (mActive) {
                     mActive = false;
                     mLastRadioState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
-                    UiccController.updateInternalIccState(
-                            mContext, IccCardConstants.State.ABSENT, null, mPhoneId,
-                            true /* special notification for absent card in an inactive slot */);
                     mPhoneId = INVALID_PHONE_ID;
                     nullifyUiccCard(true /* sim state is unknown */);
                 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 18740c1..db31f37 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -181,7 +181,7 @@
         doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController)
                 .getActiveSubIdList(/*visibleOnly*/false);
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -207,8 +207,7 @@
                 .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
         doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController)
                 .getActiveSubIdList(/*visibleOnly*/false);
-        mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1, true);
+        mUpdater.updateInternalIccStateForInactiveSlot(FAKE_SUB_ID_1, null);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -228,7 +227,7 @@
     @SmallTest
     public void testSimUnknown() throws Exception {
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertFalse(mUpdater.isSubInfoInitialized());
@@ -245,7 +244,7 @@
     @SmallTest
     public void testSimNotReady() throws Exception {
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertFalse(mUpdater.isSubInfoInitialized());
@@ -265,7 +264,7 @@
         doReturn(true).when(mIccCard).isEmptyProfile();
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -285,7 +284,7 @@
     @SmallTest
     public void testSimError() throws Exception {
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -302,7 +301,7 @@
     @SmallTest
     public void testWrongSimState() throws Exception {
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_IMSI, null, 2, false);
+                IccCardConstants.INTENT_VALUE_ICC_IMSI, null, 2);
 
         processAllMessages();
         assertFalse(mUpdater.isSubInfoInitialized());
@@ -327,7 +326,7 @@
                 true);
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -393,7 +392,7 @@
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
                 .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -420,7 +419,7 @@
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
                 .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -470,7 +469,7 @@
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
                 .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
 
         processAllMessages();
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(anyString(), anyInt());
@@ -485,7 +484,7 @@
         doReturn("89012604200000000001").when(mIccRecord).getFullIccId();
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);
 
         processAllMessages();
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
@@ -507,7 +506,7 @@
                 new String[]{"89012604200000000000"});
 
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
 
         processAllMessages();
         assertTrue(mUpdater.isSubInfoInitialized());
@@ -657,7 +656,7 @@
 
         // Mock sending a sim loaded for SIM 1
         mUpdater.updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_LOADED, "TESTING", FAKE_SUB_ID_1, false);
+                IccCardConstants.INTENT_VALUE_ICC_LOADED, "TESTING", FAKE_SUB_ID_1);
 
         processAllMessages();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index e6e7fb8..2f2b582 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -155,7 +155,7 @@
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
         assertEquals(iss.iccid, mUiccSlot.getIccId());
         verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId, false);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
 
         // update slot to active
         mUiccSlot.update(mSimulatedCommands, iss, 0 /* slotIndex */);
@@ -243,7 +243,7 @@
         mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
         verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId, false);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
         assertNull(mUiccSlot.getUiccCard());
     }
@@ -272,11 +272,10 @@
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
 
         // assert that we tried to update subscriptions
-        verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, activeIss.logicalSlotIndex, true);
+        verify(mSubInfoRecordUpdater).updateInternalIccStateForInactiveSlot(
+                activeIss.logicalSlotIndex, inactiveIss.iccid);
     }
 
-
     @Test
     @SmallTest
     public void testUiccSlotCreateAndDispose() {
@@ -296,7 +295,7 @@
         mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
         verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId, false);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
         verify(mUiccProfile).dispose();
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
         assertNull(mUiccSlot.getUiccCard());
@@ -321,7 +320,7 @@
 
         // Verify that UNKNOWN state is sent to SubscriptionInfoUpdater in this case.
         verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, phoneId, false);
+                IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, phoneId);
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
         assertNull(mUiccSlot.getUiccCard());
 
@@ -331,7 +330,7 @@
 
         // Verify that ABSENT state is sent to SubscriptionInfoUpdater in this case.
         verify(mSubInfoRecordUpdater).updateInternalIccState(
-                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId, false);
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
         assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
         assertNull(mUiccSlot.getUiccCard());
     }