Merge changes Ia8496a8d,Ic011551f,I13ff9bb0

* changes:
  In PhoneFactory, dynamically allocate 2nd phone when switching to dsds
  Have UiccController compitible with dynamicy ss <-> ds switch
  Update ProxyController to be compatible with dynamic ss <-> ds switch
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index df73ef8..2ad6056 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -30,6 +30,7 @@
 import android.os.storage.StorageManager;
 import android.telephony.PhoneCapability;
 import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -130,19 +131,6 @@
     }
 
     /**
-     * Whether the phoneId has a corresponding active slot / logical modem. If a DSDS capable
-     * device is in single SIM mode, phoneId=1 is valid but not active.
-     *
-     * TODO: b/139642279 combine with SubscriptionManager#isValidPhoneId when phone objects
-     * are dynamically allocated instead of always based on getMaxPhoneCount.
-     * @hide
-     */
-    public static boolean isPhoneActive(int phoneId) {
-        // Currently it simply depends on getPhoneCount. In future it can be generalized.
-        return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
-    }
-
-    /**
      * Handler class to handle callbacks
      */
     private final class ConfigManagerHandler extends Handler {
@@ -366,11 +354,12 @@
             pm.reboot("Multi-SIM config changed.");
         } else {
             log("onMultiSimConfigChanged: Rebooting is not required.");
+            mMi.notifyPhoneFactoryOnMultiSimConfigChanged(mContext, numOfActiveModems);
             broadcastMultiSimConfigChange(numOfActiveModems);
             // Register to RIL service if needed.
             for (int i = 0; i < mPhones.length; i++) {
                 Phone phone = mPhones[i];
-                phone.mCi.onSlotActiveStatusChange(isPhoneActive(i));
+                phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(i));
             }
         }
     }
@@ -426,6 +415,9 @@
      */
     @VisibleForTesting
     public static class MockableInterface {
+        /**
+         * Wrapper function to decide whether reboot is required for modem config change.
+         */
         @VisibleForTesting
         public boolean isRebootRequiredForModemConfigChange() {
             String rebootRequired = SystemProperties.get(
@@ -434,6 +426,9 @@
             return !rebootRequired.equals("false");
         }
 
+        /**
+         * Wrapper function to call setMultiSimProperties.
+         */
         @VisibleForTesting
         public void setMultiSimProperties(int numOfActiveModems) {
             String multiSimConfig;
@@ -451,6 +446,15 @@
             log("setMultiSimProperties to " + multiSimConfig);
             SystemProperties.set(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG, multiSimConfig);
         }
+
+        /**
+         * Wrapper function to call PhoneFactory.onMultiSimConfigChanged.
+         */
+        @VisibleForTesting
+        public void notifyPhoneFactoryOnMultiSimConfigChanged(
+                Context context, int numOfActiveModems) {
+            PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems);
+        }
     }
 
     private static void log(String s) {
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 0147fef..8a84921 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
+import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE;
+
+import static java.util.Arrays.copyOf;
+
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -145,7 +150,7 @@
                 /* In case of multi SIM mode two instances of Phone, RIL are created,
                    where as in single SIM mode only instance. isMultiSimEnabled() function checks
                    whether it is single SIM or multi SIM mode */
-                int numPhones = TelephonyManager.getDefault().getSupportedModemCount();
+                int numPhones = TelephonyManager.getDefault().getActiveModemCount();
 
                 int[] networkModes = new int[numPhones];
                 sPhones = new Phone[numPhones];
@@ -164,7 +169,7 @@
 
                 // Instantiate UiccController so that all other classes can just
                 // call getInstance()
-                sUiccController = UiccController.make(context, sCommandsInterfaces);
+                sUiccController = UiccController.make(context);
 
                 Rlog.i(LOG_TAG, "Creating SubscriptionController");
                 SubscriptionController sc = SubscriptionController.init(context);
@@ -177,28 +182,13 @@
                 }
 
                 for (int i = 0; i < numPhones; i++) {
-                    Phone phone = null;
-                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
-                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
-                        phone = new GsmCdmaPhone(context,
-                                sCommandsInterfaces[i], sPhoneNotifier, i,
-                                PhoneConstants.PHONE_TYPE_GSM,
-                                TelephonyComponentFactory.getInstance());
-                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
-                        phone = new GsmCdmaPhone(context,
-                                sCommandsInterfaces[i], sPhoneNotifier, i,
-                                PhoneConstants.PHONE_TYPE_CDMA_LTE,
-                                TelephonyComponentFactory.getInstance());
-                    }
-                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
-
-                    sPhones[i] = phone;
+                    sPhones[i] = createPhone(context, i);
                 }
 
                 // Set the default phone in base class.
                 // FIXME: This is a first best guess at what the defaults will be. It
                 // FIXME: needs to be done in a more controlled manner in the future.
-                sPhone = sPhones[0];
+                if (numPhones > 0) sPhone = sPhones[0];
 
                 // Ensure that we have a default SMS app. Requesting the app with
                 // updateIfNeeded set to true is enough to configure a default SMS app.
@@ -218,7 +208,6 @@
                 Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
                 sSubInfoRecordUpdater = new SubscriptionInfoUpdater(
                         BackgroundThread.get().getLooper(), context, sCommandsInterfaces);
-                sc.updatePhonesAvailability(sPhones);
 
                 // Only bring up IMS if the device supports having an IMS stack.
                 if (context.getPackageManager().hasSystemFeature(
@@ -260,7 +249,7 @@
                         sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
                         sPhones);
 
-                sProxyController = ProxyController.getInstance(context, sPhones, sPhoneSwitcher);
+                sProxyController = ProxyController.getInstance(context);
 
                 sIntentBroadcaster = IntentBroadcaster.getInstance(context);
 
@@ -274,6 +263,53 @@
         }
     }
 
+    /**
+     * Upon single SIM to dual SIM switch or vice versa, we dynamically allocate or de-allocate
+     * Phone and CommandInterface objects.
+     * @param context
+     * @param activeModemCount
+     */
+    public static void onMultiSimConfigChanged(Context context, int activeModemCount) {
+        synchronized (sLockProxyPhones) {
+            int prevActiveModemCount = sPhones.length;
+            if (prevActiveModemCount == activeModemCount) return;
+
+            // TODO: clean up sPhones, sCommandsInterfaces and sTelephonyNetworkFactories objects.
+            // Currently we will not clean up the 2nd Phone object, so that it can be re-used if
+            // user switches back.
+            if (prevActiveModemCount > activeModemCount) return;
+
+            sPhones = copyOf(sPhones, activeModemCount);
+            sCommandsInterfaces = copyOf(sCommandsInterfaces, activeModemCount);
+            sTelephonyNetworkFactories = copyOf(sTelephonyNetworkFactories, activeModemCount);
+
+            int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
+            for (int i = prevActiveModemCount; i < activeModemCount; i++) {
+                sCommandsInterfaces[i] = new RIL(context, RILConstants.PREFERRED_NETWORK_MODE,
+                        cdmaSubscription, i);
+                sPhones[i] = createPhone(context, i);
+                if (context.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELEPHONY_IMS)) {
+                    sPhones[i].startMonitoringImsService();
+                }
+                sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
+                        Looper.myLooper(), sPhones[i]);
+            }
+        }
+    }
+
+    private static Phone createPhone(Context context, int phoneId) {
+        int phoneType = TelephonyManager.getPhoneType(RILConstants.PREFERRED_NETWORK_MODE);
+        Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " phoneId = " + phoneId);
+
+        // We always use PHONE_TYPE_CDMA_LTE now.
+        if (phoneType == PHONE_TYPE_CDMA) phoneType = PHONE_TYPE_CDMA_LTE;
+
+        return new GsmCdmaPhone(context,
+                sCommandsInterfaces[phoneId], sPhoneNotifier, phoneId, phoneType,
+                TelephonyComponentFactory.getInstance());
+    }
+
     @UnsupportedAppUsage
     public static Phone getDefaultPhone() {
         synchronized (sLockProxyPhones) {
@@ -459,6 +495,15 @@
     }
 
     /**
+     * Get Command Interfaces.
+     */
+    public static CommandsInterface[] getCommandsInterfaces() {
+        synchronized (sLockProxyPhones) {
+            return sCommandsInterfaces;
+        }
+    }
+
+    /**
      * Adds a local log category.
      *
      * Only used within the telephony process.  Use localLog to add log entries.
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 8b8425a..363e8ff 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -43,13 +43,10 @@
     private static final boolean VDBG = false; // STOPSHIP if true
 
     @UnsupportedAppUsage
-    private final Phone[] mPhone;
-    @UnsupportedAppUsage
     private final Context mContext;
     private final AppOpsManager mAppOps;
 
-    public PhoneSubInfoController(Context context, Phone[] phone) {
-        mPhone = phone;
+    public PhoneSubInfoController(Context context) {
         if (ServiceManager.getService("iphonesubinfo") == null) {
             ServiceManager.addService("iphonesubinfo", this);
         }
@@ -221,7 +218,7 @@
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             phoneId = 0;
         }
-        return mPhone[phoneId];
+        return PhoneFactory.getPhone(phoneId);
     }
 
     /**
@@ -465,7 +462,7 @@
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             phoneId = 0;
         }
-        final Phone phone = mPhone[phoneId];
+        final Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone == null) {
             return null;
         }
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 54fcac9..69f8a0b 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static java.util.Arrays.copyOf;
+
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +32,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ims.RcsMessageController;
 
 import java.util.ArrayList;
@@ -40,11 +43,13 @@
 public class ProxyController {
     static final String LOG_TAG = "ProxyController";
 
-    private static final int EVENT_NOTIFICATION_RC_CHANGED        = 1;
+    private static final int EVENT_NOTIFICATION_RC_CHANGED  = 1;
     private static final int EVENT_START_RC_RESPONSE        = 2;
     private static final int EVENT_APPLY_RC_RESPONSE        = 3;
     private static final int EVENT_FINISH_RC_RESPONSE       = 4;
     private static final int EVENT_TIMEOUT                  = 5;
+    @VisibleForTesting
+    public static final int EVENT_MULTI_SIM_CONFIG_CHANGED  = 6;
 
     private static final int SET_RC_STATUS_IDLE             = 0;
     private static final int SET_RC_STATUS_STARTING         = 1;
@@ -105,9 +110,9 @@
 
 
     //***** Class Methods
-    public static ProxyController getInstance(Context context, Phone[] phone, PhoneSwitcher ps) {
+    public static ProxyController getInstance(Context context) {
         if (sProxyController == null) {
-            sProxyController = new ProxyController(context, phone, ps);
+            sProxyController = new ProxyController(context);
         }
         return sProxyController;
     }
@@ -117,17 +122,17 @@
         return sProxyController;
     }
 
-    private ProxyController(Context context, Phone[] phone, PhoneSwitcher phoneSwitcher) {
+    private ProxyController(Context context) {
         logd("Constructor - Enter");
 
         mContext = context;
-        mPhones = phone;
-        mPhoneSwitcher = phoneSwitcher;
+        mPhones = PhoneFactory.getPhones();
+        mPhoneSwitcher = PhoneSwitcher.getInstance();
 
         RcsMessageController.init(context);
 
-        mUiccPhoneBookController = new UiccPhoneBookController(mPhones);
-        mPhoneSubInfoController = new PhoneSubInfoController(mContext, mPhones);
+        mUiccPhoneBookController = new UiccPhoneBookController();
+        mPhoneSubInfoController = new PhoneSubInfoController(mContext);
         mSmsController = new SmsController(mContext);
         mSetRadioAccessFamilyStatus = new int[mPhones.length];
         mNewRadioAccessFamily = new int[mPhones.length];
@@ -146,6 +151,9 @@
             mPhones[i].registerForRadioCapabilityChanged(
                     mHandler, EVENT_NOTIFICATION_RC_CHANGED, null);
         }
+
+        PhoneConfigurationManager.registerForMultiSimConfigChange(
+                mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
         logd("Constructor - Exit");
     }
 
@@ -201,7 +209,7 @@
      */
     public boolean setRadioCapability(RadioAccessFamily[] rafs) {
         if (rafs.length != mPhones.length) {
-            throw new RuntimeException("Length of input rafs must equal to total phone count");
+            return false;
         }
         // Check if there is any ongoing transaction and throw an exception if there
         // is one as this is a programming error.
@@ -291,7 +299,8 @@
         return true;
     }
 
-    private Handler mHandler = new Handler() {
+    @VisibleForTesting
+    public final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             logd("handleMessage msg.what=" + msg.what);
@@ -316,12 +325,37 @@
                     onTimeoutRadioCapability(msg);
                     break;
 
+                case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                    onMultiSimConfigChanged();
+                    break;
+
                 default:
                     break;
             }
         }
     };
 
+    private void onMultiSimConfigChanged() {
+        int oldPhoneCount = mPhones.length;
+        mPhones = PhoneFactory.getPhones();
+
+        // Re-size arrays.
+        mSetRadioAccessFamilyStatus = copyOf(mSetRadioAccessFamilyStatus, mPhones.length);
+        mNewRadioAccessFamily = copyOf(mNewRadioAccessFamily, mPhones.length);
+        mOldRadioAccessFamily = copyOf(mOldRadioAccessFamily, mPhones.length);
+        mCurrentLogicalModemIds = copyOf(mCurrentLogicalModemIds, mPhones.length);
+        mNewLogicalModemIds = copyOf(mNewLogicalModemIds, mPhones.length);
+
+        // Clear to be sure we're in the initial state
+        clearTransaction();
+
+        // Register radio cap change for new phones.
+        for (int i = oldPhoneCount; i < mPhones.length; i++) {
+            mPhones[i].registerForRadioCapabilityChanged(
+                    mHandler, EVENT_NOTIFICATION_RC_CHANGED, null);
+        }
+    }
+
     /**
      * Handle START response
      * @param msg obj field isa RadioCapability
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 4d7a687..e7201e4 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -91,6 +91,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyManager.PrefNetworkMode;
@@ -413,7 +414,7 @@
     /** Returns a {@link IRadio} instance or null if the service is not available. */
     @VisibleForTesting
     public synchronized IRadio getRadioProxy(Message result) {
-        if (!PhoneConfigurationManager.isPhoneActive(mPhoneId)) return null;
+        if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null;
         if (!mIsMobileNetworkSupported) {
             if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
             if (result != null) {
@@ -510,13 +511,15 @@
             // Try to connect to RIL services and set response functions.
             getRadioProxy(null);
             getOemHookProxy(null);
+        } else {
+            resetProxyAndRequestList();
         }
     }
 
     /** Returns an {@link IOemHook} instance or null if the service is not available. */
     @VisibleForTesting
     public synchronized IOemHook getOemHookProxy(Message result) {
-        if (!PhoneConfigurationManager.isPhoneActive(mPhoneId)) return null;
+        if (!SubscriptionManager.isValidPhoneId((mPhoneId))) return null;
         if (!mIsMobileNetworkSupported) {
             if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
             if (result != null) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 24bb42d..ab24988 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -133,7 +133,6 @@
 
     /** The singleton instance. */
     private static SubscriptionController sInstance = null;
-    protected static Phone[] sPhones;
     @UnsupportedAppUsage
     protected Context mContext;
     protected TelephonyManager mTelephonyManager;
@@ -1185,7 +1184,7 @@
                 }
 
                 // Once the records are loaded, notify DcTracker
-                sPhones[slotIndex].updateDataConnectionTracker();
+                PhoneFactory.getPhone(slotIndex).updateDataConnectionTracker();
 
                 if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubIds.size());
             }
@@ -2244,7 +2243,7 @@
             }
 
             ProxyController proxyController = ProxyController.getInstance();
-            int len = sPhones.length;
+            int len = TelephonyManager.from(mContext).getActiveModemCount();
             logdl("[setDefaultDataSubId] num phones=" + len + ", subId=" + subId);
 
             if (SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -2252,7 +2251,7 @@
                 RadioAccessFamily[] rafs = new RadioAccessFamily[len];
                 boolean atLeastOneMatch = false;
                 for (int phoneId = 0; phoneId < len; phoneId++) {
-                    Phone phone = sPhones[phoneId];
+                    Phone phone = PhoneFactory.getPhone(phoneId);
                     int raf;
                     int id = phone.getSubId();
                     if (id == subId) {
@@ -2293,11 +2292,11 @@
     @UnsupportedAppUsage
     private void updateAllDataConnectionTrackers() {
         // Tell Phone Proxies to update data connection tracker
-        int len = sPhones.length;
-        if (DBG) logd("[updateAllDataConnectionTrackers] sPhones.length=" + len);
+        int len = TelephonyManager.from(mContext).getActiveModemCount();
+        if (DBG) logd("[updateAllDataConnectionTrackers] activeModemCount=" + len);
         for (int phoneId = 0; phoneId < len; phoneId++) {
             if (DBG) logd("[updateAllDataConnectionTrackers] phoneId=" + phoneId);
-            sPhones[phoneId].updateDataConnectionTracker();
+            PhoneFactory.getPhone(phoneId).updateDataConnectionTracker();
         }
     }
 
@@ -2433,10 +2432,6 @@
         }
     }
 
-    public void updatePhonesAvailability(Phone[] phones) {
-        sPhones = phones;
-    }
-
     private synchronized ArrayList<Integer> getActiveSubIdArrayList() {
         // Clone the sub id list so it can't change out from under us while iterating
         List<Entry<Integer, ArrayList<Integer>>> simInfoList =
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
index 5a4d480..6c9fdbe 100644
--- a/src/java/com/android/internal/telephony/UiccPhoneBookController.java
+++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -25,22 +25,17 @@
 import com.android.internal.telephony.IIccPhoneBook;
 import com.android.internal.telephony.uicc.AdnRecord;
 
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.lang.NullPointerException;
 import java.util.List;
 
 public class UiccPhoneBookController extends IIccPhoneBook.Stub {
     private static final String TAG = "UiccPhoneBookController";
-    @UnsupportedAppUsage
-    private Phone[] mPhone;
 
     /* only one UiccPhoneBookController exists */
     @UnsupportedAppUsage
-    public UiccPhoneBookController(Phone[] phone) {
+    public UiccPhoneBookController() {
         if (ServiceManager.getService("simphonebook") == null) {
                ServiceManager.addService("simphonebook", this);
         }
-        mPhone = phone;
     }
 
     @Override
@@ -139,7 +134,7 @@
 
         int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
         try {
-            return mPhone[phoneId].getIccPhoneBookInterfaceManager();
+            return PhoneFactory.getPhone(phoneId).getIccPhoneBookInterfaceManager();
         } catch (NullPointerException e) {
             Rlog.e(TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
             e.printStackTrace(); //To print stack trace
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 6112dda..a7bce9c 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -19,6 +19,8 @@
 import static android.telephony.TelephonyManager.UNINITIALIZED_CARD_ID;
 import static android.telephony.TelephonyManager.UNSUPPORTED_CARD_ID;
 
+import static java.util.Arrays.copyOf;
+
 import android.annotation.UnsupportedAppUsage;
 import android.app.BroadcastOptions;
 import android.content.Context;
@@ -43,6 +45,7 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConfigurationManager;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RadioConfig;
@@ -121,6 +124,7 @@
     private static final int EVENT_RADIO_UNAVAILABLE = 7;
     private static final int EVENT_SIM_REFRESH = 8;
     private static final int EVENT_EID_READY = 9;
+    private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 10;
 
     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
     @UnsupportedAppUsage
@@ -187,20 +191,23 @@
     // LocalLog buffer to hold important SIM related events for debugging
     static LocalLog sLocalLog = new LocalLog(100);
 
-    public static UiccController make(Context c, CommandsInterface[] ci) {
+    /**
+     * API to make UiccController singleton if not already created.
+     */
+    public static UiccController make(Context c) {
         synchronized (mLock) {
             if (mInstance != null) {
                 throw new RuntimeException("UiccController.make() should only be called once");
             }
-            mInstance = new UiccController(c, ci);
+            mInstance = new UiccController(c);
             return mInstance;
         }
     }
 
-    private UiccController(Context c, CommandsInterface []ci) {
+    private UiccController(Context c) {
         if (DBG) log("Creating UiccController");
         mContext = c;
-        mCis = ci;
+        mCis = PhoneFactory.getCommandsInterfaces();
         if (DBG) {
             String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
                     com.android.internal.R.integer.config_num_physical_slots);
@@ -216,7 +223,7 @@
         }
 
         mUiccSlots = new UiccSlot[numPhysicalSlots];
-        mPhoneIdToSlotId = new int[ci.length];
+        mPhoneIdToSlotId = new int[mCis.length];
         Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
         if (VDBG) logPhoneIdToSlotIdMapping();
         mRadioConfig = RadioConfig.getInstance(mContext);
@@ -245,6 +252,9 @@
         mEuiccSlots = mContext.getResources()
                 .getIntArray(com.android.internal.R.array.non_removable_euicc_slots);
         mHasBuiltInEuicc = hasBuiltInEuicc();
+
+        PhoneConfigurationManager.registerForMultiSimConfigChange(
+                this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
     }
 
     /**
@@ -519,6 +529,9 @@
                     if (DBG) log("Received EVENT_EID_READY");
                     onEidReady(ar, phoneId);
                     break;
+                case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                    if (DBG) log("Received EVENT_MULTI_SIM_CONFIG_CHANGED");
+                    onMultiSimConfigChanged();
                 default:
                     Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
                     break;
@@ -526,6 +539,32 @@
         }
     }
 
+    private void onMultiSimConfigChanged() {
+        int prevActiveModemCount = mCis.length;
+        mCis = PhoneFactory.getCommandsInterfaces();
+
+        // Resize array.
+        mPhoneIdToSlotId = copyOf(mPhoneIdToSlotId, mCis.length);
+
+        for (int i = prevActiveModemCount; i < mCis.length; i++) {
+            mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
+
+            /*
+             * To support FDE (deprecated), additional check is needed:
+             *
+             * if (!StorageManager.inCryptKeeperBounce()) {
+             *     mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
+             * } else {
+             *     mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
+             * }
+             */
+            mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
+
+            mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
+            mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
+        }
+    }
+
     private Integer getCiIndex(Message msg) {
         AsyncResult ar;
         Integer index = new Integer(PhoneConstants.DEFAULT_SLOT_INDEX);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 6f01a47..08e5a0a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -168,6 +168,7 @@
 
         // Verify set system property being called.
         verify(mMi).setMultiSimProperties(MODEM_COUNT_DUAL_MODEM);
+        verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(MODEM_COUNT_DUAL_MODEM));
 
         // Capture and verify registration notification.
         verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index a69a9a6..69c2ff7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -66,8 +66,8 @@
 
         mAppOsMgr = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
 
-        mPhoneSubInfoControllerUT = new PhoneSubInfoController(mContext,
-                new Phone[]{mPhone, mSecondPhone});
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mSecondPhone});
+        mPhoneSubInfoControllerUT = new PhoneSubInfoController(mContext);
 
         setupMocksForTelephonyPermissions();
         // TelephonyPermissions will query the READ_DEVICE_IDENTIFIERS op from AppOpManager to
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
new file mode 100644
index 0000000..128e6d8
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.ProxyController.EVENT_MULTI_SIM_CONFIG_CHANGED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProxyControllerTest extends TelephonyTest {
+    @Mock
+    Phone mPhone2;
+
+    ProxyController mProxyController;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        replaceInstance(ProxyController.class, "sProxyController", null, null);
+        mProxyController = ProxyController.getInstance(mContext);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Restore system properties.
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testMultiSimConfigChange() throws Exception {
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(int.class);
+
+        // Switch to dual-SIM and send multi sim config change callback.
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone2});
+        Message.obtain(mProxyController.mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED).sendToTarget();
+        processAllMessages();
+        verify(mPhone2).registerForRadioCapabilityChanged(any(), anyInt(), any());
+
+        // Switch to single-SIM and verify there's at least no crash.
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+        Message.obtain(mProxyController.mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED).sendToTarget();
+        processAllMessages();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index ce3281d..75b3534 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -97,8 +97,6 @@
 
         doReturn(1).when(mProxyController).getMaxRafSupported();
         mContextFixture.putIntArrayResource(com.android.internal.R.array.sim_colors, new int[]{5});
-
-        mSubscriptionControllerUT.updatePhonesAvailability(new Phone[] {mPhone});
     }
 
     @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index b6516fa..6c2faf0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -49,7 +49,6 @@
 import android.os.MessageQueue;
 import android.os.RegistrantList;
 import android.os.ServiceManager;
-import android.telephony.TelephonyRegistryManager;
 import android.provider.BlockedNumberContract;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -58,6 +57,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsCallProfile;
@@ -620,6 +620,8 @@
         replaceInstance(MultiSimSettingController.class, "sInstance", null,
                 mMultiSimSettingController);
         replaceInstance(SubscriptionInfoUpdater.class, "sIsSubInfoInitialized", null, true);
+        replaceInstance(PhoneFactory.class, "sCommandsInterfaces", null,
+                new CommandsInterface[] {mSimulatedCommands});
 
         assertNotNull("Failed to set up SubscriptionController singleton",
                 SubscriptionController.getInstance());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
index 03ef19d..d3804d3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -27,7 +27,6 @@
 import android.telephony.SubscriptionInfo;
 
 import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyIntents;
@@ -243,10 +242,6 @@
         }
     }
     @Override
-    public void updatePhonesAvailability(Phone[] phones) {
-        throw new RuntimeException("not implemented");
-    }
-    @Override
     public int[] getActiveSubIdList(boolean visibleOnly) {
         throw new RuntimeException("not implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 804bdf1..336267c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -39,7 +39,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.uicc.euicc.EuiccCard;
 
@@ -52,6 +51,7 @@
 
 import java.util.ArrayList;
 
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class UiccControllerTest extends TelephonyTest {
@@ -109,8 +109,7 @@
         // for testing we pretend slotIndex is set. In reality it would be invalid on older versions
         // (before 1.2) of hal
         mIccCardStatus.physicalSlotIndex = 0;
-        mUiccControllerUT = UiccController.make(mContext,
-            new CommandsInterface[]{mSimulatedCommands});
+        mUiccControllerUT = UiccController.make(mContext);
         // reset sLastSlotStatus so that onGetSlotStatusDone always sees a change in the slot status
         mUiccControllerUT.sLastSlotStatus = null;
         processAllMessages();
@@ -131,8 +130,7 @@
                 com.android.internal.R.array.non_removable_euicc_slots,
                 nonRemovableEuiccSlots);
         replaceInstance(UiccController.class, "mInstance", null, null);
-        mUiccControllerUT = UiccController.make(mContext,
-            new CommandsInterface[]{mSimulatedCommands});
+        mUiccControllerUT = UiccController.make(mContext);
         processAllMessages();
     }