Merge "Add new vendor prefix files"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index d1f8ea2..18006710 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -3670,7 +3670,7 @@
                 cdmaApplication.getType() == AppType.APPTYPE_RUIM);
     }
 
-    private void phoneObjectUpdater(int newVoiceRadioTech) {
+    protected void phoneObjectUpdater(int newVoiceRadioTech) {
         logd("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech);
 
         // Check for a voice over LTE/NR replacement
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 9b181d3..5aa6237 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -169,8 +169,9 @@
                 sUiccController = UiccController.make(context);
 
                 Rlog.i(LOG_TAG, "Creating SubscriptionController");
-                SubscriptionController sc = SubscriptionController.init(context);
-                MultiSimSettingController.init(context, sc);
+                TelephonyComponentFactory.getInstance().inject(SubscriptionController.class.
+                        getName()).initSubscriptionController(context);
+                MultiSimSettingController.init(context, SubscriptionController.getInstance());
 
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_EUICC)) {
@@ -228,7 +229,9 @@
                 int maxActivePhones = sPhoneConfigurationManager
                         .getNumberOfModemsWithSimultaneousDataConnections();
 
-                sPhoneSwitcher = PhoneSwitcher.make(maxActivePhones, sContext, Looper.myLooper());
+                sPhoneSwitcher = TelephonyComponentFactory.getInstance().inject(
+                        PhoneSwitcher.class.getName()).
+                        makePhoneSwitcher(maxActivePhones, sContext, Looper.myLooper());
 
                 sProxyController = ProxyController.getInstance(context);
 
@@ -285,8 +288,10 @@
 
         // We always use PHONE_TYPE_CDMA_LTE now.
         if (phoneType == PHONE_TYPE_CDMA) phoneType = PHONE_TYPE_CDMA_LTE;
+        TelephonyComponentFactory injectedComponentFactory =
+                TelephonyComponentFactory.getInstance().inject(GsmCdmaPhone.class.getName());
 
-        return new GsmCdmaPhone(context,
+        return injectedComponentFactory.makePhone(context,
                 sCommandsInterfaces[phoneId], sPhoneNotifier, phoneId, phoneType,
                 TelephonyComponentFactory.getInstance());
     }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index b8b4a8c..b2ccb99 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -446,7 +446,7 @@
 
     //Common
     @UnsupportedAppUsage
-    private final GsmCdmaPhone mPhone;
+    protected final GsmCdmaPhone mPhone;
 
     private CellIdentity mCellIdentity;
     private CellIdentity mNewCellIdentity;
@@ -2076,7 +2076,7 @@
         }
     }
 
-    void handlePollStateResultMessage(int what, AsyncResult ar) {
+    protected void handlePollStateResultMessage(int what, AsyncResult ar) {
         int ints[];
         switch (what) {
             case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION: {
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 7d6a073..00690ef 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -133,7 +133,7 @@
     protected final Object mLock = new Object();
 
     /** The singleton instance. */
-    private static SubscriptionController sInstance = null;
+    protected static SubscriptionController sInstance = null;
     @UnsupportedAppUsage
     protected Context mContext;
     protected TelephonyManager mTelephonyManager;
@@ -143,7 +143,7 @@
 
     // Each slot can have multiple subs.
     private static Map<Integer, ArrayList<Integer>> sSlotIndexToSubIds = new ConcurrentHashMap<>();
-    private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    protected static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     @UnsupportedAppUsage
     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
 
@@ -247,7 +247,7 @@
     }
 
     @UnsupportedAppUsage
-    private void enforceModifyPhoneState(String message) {
+    protected void enforceModifyPhoneState(String message) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MODIFY_PHONE_STATE, message);
     }
@@ -2150,7 +2150,7 @@
     }
 
     @UnsupportedAppUsage
-    private void logdl(String msg) {
+    protected void logdl(String msg) {
         logd(msg);
         mLocalLog.log(msg);
     }
@@ -2383,7 +2383,7 @@
      * the first sub is set as default subscription
      */
     @UnsupportedAppUsage
-    private void setDefaultFallbackSubId(int subId, int subscriptionType) {
+    protected void setDefaultFallbackSubId(int subId, int subscriptionType) {
         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
         }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index b84a407..979f29c 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -420,4 +420,20 @@
     public DataEnabledSettings makeDataEnabledSettings(Phone phone) {
         return new DataEnabledSettings(phone);
     }
+
+    public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+            int phoneId, int precisePhoneType,
+            TelephonyComponentFactory telephonyComponentFactory) {
+        return new GsmCdmaPhone(context, ci, notifier, phoneId, precisePhoneType,
+                telephonyComponentFactory);
+    }
+
+    public SubscriptionController initSubscriptionController(Context c) {
+        return SubscriptionController.init(c);
+    }
+
+    public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context,
+            Looper looper) {
+        return PhoneSwitcher.make(maxDataAttachModemCount, context, looper);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index cb0d0a9..0251e90 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -586,9 +586,9 @@
     private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
 
     // member variables
-    private final Phone mPhone;
+    protected final Phone mPhone;
     private final UiccController mUiccController;
-    private final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
+    protected final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
     private DctConstants.Activity mActivity = DctConstants.Activity.NONE;
     private DctConstants.State mState = DctConstants.State.IDLE;
     private final Handler mDataConnectionTracker;
@@ -621,7 +621,7 @@
     private PendingIntent mReconnectIntent = null;
 
     // When false we will not auto attach and manually attaching is required.
-    private boolean mAutoAttachOnCreationConfig = false;
+    protected boolean mAutoAttachOnCreationConfig = false;
     private AtomicBoolean mAutoAttachEnabled = new AtomicBoolean(false);
 
     // State of screen
@@ -1495,7 +1495,7 @@
     }
 
     // arg for setupDataOnAllConnectableApns
-    private enum RetryFailures {
+    protected enum RetryFailures {
         // retry failed networks always (the old default)
         ALWAYS,
         // retry only when a substantial change has occurred.  Either:
@@ -1504,7 +1504,7 @@
         ONLY_ON_CHANGE
     };
 
-    private void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
+    protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
         if (VDBG) log("setupDataOnAllConnectableApns: " + reason);
 
         if (DBG && !VDBG) {
@@ -2056,7 +2056,7 @@
         return true;
     }
 
-    private void setInitialAttachApn() {
+    protected void setInitialAttachApn() {
         ApnSetting iaApnSetting = null;
         ApnSetting defaultApnSetting = null;
         ApnSetting firstNonEmergencyApnSetting = null;
@@ -2088,6 +2088,12 @@
             }
         }
 
+        if ((iaApnSetting == null) && (defaultApnSetting == null) &&
+                !allowInitialAttachForOperator()) {
+            log("Abort Initial attach");
+            return;
+        }
+
         // The priority of apn candidates from highest to lowest is:
         //   1) APN_TYPE_IA (Initial Attach)
         //   2) mPreferredApn, i.e. the current preferred apn
@@ -2120,6 +2126,10 @@
         }
     }
 
+    protected boolean allowInitialAttachForOperator() {
+        return true;
+    }
+
     /**
      * Handles changes to the APN database.
      */
@@ -2269,7 +2279,7 @@
         removeMessages(DctConstants.EVENT_DATA_RECONNECT, apnContext);
     }
 
-    private void onRecordsLoadedOrSubIdChanged() {
+    protected void onRecordsLoadedOrSubIdChanged() {
         if (DBG) log("onRecordsLoadedOrSubIdChanged: createAllApnList");
         if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
             // Auto attach is for cellular only.
@@ -3104,7 +3114,7 @@
         return true;
     }
 
-    private void setDataProfilesAsNeeded() {
+    protected void setDataProfilesAsNeeded() {
         if (DBG) log("setDataProfilesAsNeeded");
 
         ArrayList<DataProfile> dataProfileList = new ArrayList<>();
@@ -3131,7 +3141,7 @@
      * Based on the sim operator numeric, create a list for all possible
      * Data Connections and setup the preferredApn.
      */
-    private void createAllApnList() {
+    protected void createAllApnList() {
         mAllApnSettings.clear();
         IccRecords r = mIccRecords.get();
         String operator = (r != null) ? r.getOperatorNumeric() : "";
@@ -4069,7 +4079,7 @@
                 || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED);
     }
 
-    private void log(String s) {
+    protected void log(String s) {
         Rlog.d(mLogTag, s);
     }
 
diff --git a/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java b/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
new file mode 100644
index 0000000..0c92159
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.vendor;
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.TelephonyComponentFactory;
+
+public class VendorGsmCdmaPhone extends GsmCdmaPhone {
+    private static final String LOG_TAG = "VendorGsmCdmaPhone";
+    private static final int PROP_EVENT_START = EVENT_LAST;
+    private static final int DEFAULT_PHONE_INDEX = 0;
+
+    private boolean mIsPhoneReadySent = false;
+    private boolean mIsPhoneReadyPending = false;
+    private static int READY = 1;
+
+    public VendorGsmCdmaPhone(Context context,
+            CommandsInterface ci, PhoneNotifier notifier, int phoneId,
+            int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
+        this(context, ci, notifier, false, phoneId, precisePhoneType,
+                telephonyComponentFactory);
+    }
+
+    public VendorGsmCdmaPhone(Context context,
+            CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode, int phoneId,
+            int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
+        super(context, ci, notifier, unitTestMode, phoneId, precisePhoneType,
+                telephonyComponentFactory);
+        Rlog.d(LOG_TAG, "Constructor");
+    }
+
+    @Override
+    protected void phoneObjectUpdater(int newVoiceTech) {
+        super.phoneObjectUpdater(newVoiceTech);
+    }
+
+    @Override
+    public boolean getCallForwardingIndicator() {
+        if (!isCurrentSubValid()) {
+            return false;
+        }
+        return super.getCallForwardingIndicator();
+    }
+
+    private boolean isCurrentSubValid() {
+        boolean isUiccApplicationEnabled = true;
+        // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it above
+
+        SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
+
+        Rlog.d(LOG_TAG, "ProvisionStatus: " + isUiccApplicationEnabled + " phone id:" + mPhoneId);
+        return subscriptionManager.isActiveSubscriptionId(getSubId()) && isUiccApplicationEnabled;
+    }
+
+    public void fetchIMEI() {
+            Rlog.d(LOG_TAG, "fetching device id");
+            mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        Rlog.d(LOG_TAG, "handleMessage: Event: " + msg.what);
+        AsyncResult ar;
+        switch(msg.what) {
+
+            case EVENT_SIM_RECORDS_LOADED:
+                if(isPhoneTypeGsm()) {
+                    Rlog.d(LOG_TAG, "notify call forward indication, phone id:" + mPhoneId);
+                    notifyCallForwardingIndicator();
+                }
+
+                super.handleMessage(msg);
+                break;
+
+            case EVENT_RADIO_AVAILABLE:
+                mIsPhoneReadySent = false;
+                super.handleMessage(msg);
+                break;
+
+            case EVENT_RIL_CONNECTED:
+                mIsPhoneReadySent = false;
+                super.handleMessage(msg);
+                break;
+
+            default: {
+                super.handleMessage(msg);
+            }
+
+        }
+    }
+
+    // In DSDA, char 'D' is used as DTMF char for playing supervisory tone for G/W.
+    // For CDMA, '#' is used. A, B, C & D are also supported as DTMF digits for G/W networks.
+    @Override
+    public void startDtmf(char c) {
+        if (!(PhoneNumberUtils.is12Key(c) || (c == 'D'))) {
+            Rlog.e(LOG_TAG, "startDtmf called with invalid character '" + c + "'");
+        } else {
+            if (isPhoneTypeCdma() && c == 'D') {
+                c = '#';
+            }
+            mCi.startDtmf(c, null);
+        }
+    }
+
+    // For CDMA sendBurstDtmf is used, if dtmf char is 'D' then it with '#'
+    // since 'D' is used for SCH tone and for CDMA it has to be '#'.
+    @Override
+    public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+        Character c = dtmfString.charAt(0);
+        if(dtmfString.length() == 1 && c == 'D') {
+            dtmfString = c.toString();
+        }
+        super.sendBurstDtmf(dtmfString, on, off, onComplete);
+    }
+
+    // When OOS occurs, IMS registration may be still available so that IMS service
+    // state is also in-service, then reports in-service to upper layer.
+    // Add a precondition to merge IMS service so that notifies proper service state
+    // after IMS changes RAT.
+    @Override
+    public ServiceState getServiceState() {
+        if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) {
+            // Ensure UE has IMS service capability, then merge IMS service state.
+            // Video enabled includes WIFI video
+            final boolean isImsEnabled = mImsPhone != null && (mImsPhone.isVolteEnabled()
+                    || mImsPhone.isVideoEnabled()
+                    || mImsPhone.isWifiCallingEnabled());
+            if (isImsEnabled) {
+                return ServiceState.mergeServiceStates(
+                        ((mSST == null) ? new ServiceState() : mSST.mSS),
+                        mImsPhone.getServiceState());
+            }
+        }
+
+        if (mSST != null) {
+            return mSST.mSS;
+        } else {
+            // avoid potential NPE in EmergencyCallHelper during Phone switch
+            return new ServiceState();
+        }
+    }
+
+    private void logd(String msg) {
+        Rlog.d(LOG_TAG, "[" + mPhoneId +" ] " + msg);
+    }
+
+    private void loge(String msg) {
+        Rlog.e(LOG_TAG, "[" + mPhoneId +" ] " + msg);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java b/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
new file mode 100644
index 0000000..de9d42c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.vendor;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+
+public class VendorServiceStateTracker extends ServiceStateTracker {
+    private static final String LOG_TAG = "VendorServiceStateTracker";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;  // STOPSHIP if true
+    private static final String ACTION_MANAGED_ROAMING_IND =
+            "android.intent.action.ACTION_MANAGED_ROAMING_IND";
+
+    public VendorServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
+        super(phone,ci);
+    }
+
+    @Override
+    protected void handlePollStateResultMessage(int what, AsyncResult ar) {
+        switch (what) {
+            case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION: {
+                super.handlePollStateResultMessage(what, ar);
+                if (mPhone.isPhoneTypeGsm()) {
+                    NetworkRegistrationInfo regStates = (NetworkRegistrationInfo) ar.result;
+                    int regState = regStates.getRegistrationState();
+
+                    if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED) {
+                        int rejCode = regStates.getRejectCause();
+                        // Check if rejCode is "Persistent location update reject",
+                        if (rejCode == 10) {
+                            log(" Posting Managed roaming intent sub = "
+                                    + mPhone.getSubId());
+                            try {
+                                Intent intent =
+                                        new Intent(ACTION_MANAGED_ROAMING_IND);
+                                // component would display Dialog to perform Manual scan
+                                // if current Network selection Mode is Manual.
+                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                                intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                                        mPhone.getSubId());
+                                mPhone.getContext().startActivity(intent);
+                            } catch (ActivityNotFoundException e) {
+                                loge("unable to start activity: " + e);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+
+            default:
+                super.handlePollStateResultMessage(what, ar);
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        if (msg.what == EVENT_RADIO_STATE_CHANGED) {
+            if (mPhone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
+                setPowerStateToDesired();
+                log("Trigger as manual polling");
+                pollState();
+            } else {
+                super.handleMessage(msg);
+            }
+        } else {
+            super.handleMessage(msg);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
new file mode 100644
index 0000000..37fa9af
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.vendor;
+
+import android.content.Context;
+import android.content.Intent;
+import android.Manifest;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.Iterator;
+import java.util.List;
+
+/*
+ * Extending SubscriptionController here:
+ * To implement fall back of sms/data user preferred subId value to next
+ * available subId when current preferred SIM deactivated or removed.
+ */
+public class VendorSubscriptionController extends SubscriptionController {
+    static final String LOG_TAG = "VendorSubscriptionController";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+    private static int sNumPhones;
+
+    private static final int PROVISIONED = 1;
+    private static final int NOT_PROVISIONED = 0;
+
+    private TelecomManager mTelecomManager;
+    private TelephonyManager mTelephonyManager;
+
+    private RegistrantList mAddSubscriptionRecordRegistrants = new RegistrantList();
+
+    private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
+    /**
+     * This intent would be broadcasted when a subId/slotId pair added to the
+     * sSlotIdxToSubId hashmap.
+     */
+    private static final String ACTION_SUBSCRIPTION_RECORD_ADDED =
+            "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED";
+
+    public static VendorSubscriptionController init(Context c) {
+        synchronized (VendorSubscriptionController.class) {
+            if (sInstance == null) {
+                sInstance = new VendorSubscriptionController(c);
+            } else {
+                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
+            }
+            return (VendorSubscriptionController)sInstance;
+        }
+    }
+
+    public static VendorSubscriptionController getInstance() {
+        if (sInstance == null) {
+           Log.wtf(LOG_TAG, "getInstance null");
+        }
+
+        return (VendorSubscriptionController)sInstance;
+    }
+
+    protected VendorSubscriptionController(Context c) {
+        super(c);
+        if (DBG) logd(" init by Context");
+
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        sNumPhones = TelephonyManager.getDefault().getPhoneCount();
+    }
+
+    public void registerForAddSubscriptionRecord(Handler handler, int what, Object obj) {
+        Registrant r = new Registrant(handler, what, obj);
+        synchronized (mAddSubscriptionRecordRegistrants) {
+            mAddSubscriptionRecordRegistrants.add(r);
+            List<SubscriptionInfo> subInfoList =
+                    getActiveSubscriptionInfoList(mContext.getOpPackageName());
+            if (subInfoList != null) {
+                r.notifyRegistrant();
+            }
+        }
+    }
+
+    public void unregisterForAddSubscriptionRecord(Handler handler) {
+        synchronized (mAddSubscriptionRecordRegistrants) {
+            mAddSubscriptionRecordRegistrants.remove(handler);
+        }
+    }
+
+    @Override
+    public int addSubInfoRecord(String iccId, int slotIndex) {
+        logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "]");
+        return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+    }
+
+    @Override
+    public int addSubInfo(String uniqueId, String displayName, int slotIndex,
+            int subscriptionType) {
+
+        int retVal = super.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType);
+
+        int[] subId = getSubId(slotIndex);
+        if (subId != null && (subId.length > 0)) {
+            // When a new entry added in sSlotIdxToSubId for slotId, broadcast intent
+            logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "] = " + subId[0]);
+            mAddSubscriptionRecordRegistrants.notifyRegistrants(
+                    new AsyncResult(null, slotIndex, null));
+            Intent intent = new Intent(ACTION_SUBSCRIPTION_RECORD_ADDED);
+            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, slotIndex, subId[0]);
+            mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        }
+        return retVal;
+    }
+
+    protected boolean isRadioAvailableOnAllSubs() {
+        for (int i = 0; i < sNumPhones; i++) {
+            if (PhoneFactory.getPhone(i).mCi != null &&
+                    PhoneFactory.getPhone(i).mCi.getRadioState() ==
+                    TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    protected boolean isShuttingDown() {
+        for (int i = 0; i < sNumPhones; i++) {
+            if (PhoneFactory.getPhone(i) != null &&
+                    PhoneFactory.getPhone(i).isShuttingDown()) return true;
+        }
+        return false;
+    }
+
+    public boolean isRadioInValidState() {
+
+        // Radio Unavailable, do not updateUserPrefs. As this may happened due to SSR or RIL Crash.
+        if (!isRadioAvailableOnAllSubs()) {
+            logd(" isRadioInValidState, radio not available");
+            return false;
+        }
+
+        //Do not updateUserPrefs when Shutdown is in progress
+        if (isShuttingDown()) {
+            logd(" isRadioInValidState: device shutdown in progress ");
+            return false;
+        }
+        return true;
+    }
+
+    // If any of the voice/data/sms preference related SIM
+    // deactivated/re-activated this will update the preference
+    // with next available/activated SIM.
+    public void updateUserPreferences() {
+        SubscriptionInfo mNextActivatedSub = null;
+        int activeCount = 0;
+        if (!isRadioInValidState()) {
+            logd("Radio is in Invalid state, Ignore Updating User Preference!!!");
+            return;
+        }
+        List<SubscriptionInfo> sil = getActiveSubscriptionInfoList(mContext.getOpPackageName());
+        // If list of active subscriptions empty OR non of the SIM provisioned
+        // clear defaults preference of voice/sms/data.
+        if (sil == null || sil.size() < 1) {
+            logi("updateUserPreferences: Subscription list is empty");
+            return;
+        }
+
+        // Do not fallback to next available sub if AOSP feature
+        // "User choice of selecting data/sms fallback preference" enabled.
+        if (SystemProperties.getBoolean("persist.vendor.radio.aosp_usr_pref_sel", false)) {
+            logi("updateUserPreferences: AOSP user preference option enabled ");
+            return;
+        }
+
+        //Get num of activated Subs and next available activated sub info.
+        for (SubscriptionInfo subInfo : sil) {
+            if (getUiccProvisionStatus(subInfo.getSimSlotIndex()) == PROVISIONED) {
+                activeCount++;
+                if (mNextActivatedSub == null) mNextActivatedSub = subInfo;
+            }
+        }
+        logd("updateUserPreferences:: active sub count = " + activeCount + " dds = "
+                 + getDefaultDataSubId() + " voice = " + getDefaultVoiceSubId() +
+                 " sms = " + getDefaultSmsSubId());
+
+        // If active SUB count is 1, Always Ask Prompt to be disabled and
+        // preference fallback to the next available SUB.
+        if (activeCount == 1) {
+            setSmsPromptEnabled(false);
+        }
+
+        // TODO Set all prompt options to false ?
+
+        // in Single SIM case or if there are no activated subs available, no need to update. EXIT.
+        if ((mNextActivatedSub == null) || (getActiveSubInfoCountMax() == 1)) return;
+
+        handleDataPreference(mNextActivatedSub.getSubscriptionId());
+
+        if (!isSubProvisioned(getDefaultSmsSubId())) {
+            setDefaultSmsSubId(mNextActivatedSub.getSubscriptionId());
+        }
+        if (!isSubProvisioned(getDefaultVoiceSubId())) {
+            setDefaultVoiceSubId(mNextActivatedSub.getSubscriptionId());
+        }
+
+        // voice preference is handled in such a way that
+        // 1. Whenever current Sub is deactivated or removed It fall backs to
+        //    next available Sub.
+        // 2. When device is flashed for the first time, initial voice preference
+        //    would be set to always ask.
+        if (!isNonSimAccountFound() && activeCount == 1) {
+            final int subId = mNextActivatedSub.getSubscriptionId();
+            PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(subId);
+            logi("set default phoneaccount to  " + subId);
+            mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
+        }
+        if (!isSubProvisioned(mDefaultFallbackSubId)) {
+            setDefaultFallbackSubId(mNextActivatedSub.getSubscriptionId(),
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+        }
+
+        notifySubscriptionInfoChanged();
+        logd("updateUserPreferences: after currentDds = " + getDefaultDataSubId() + " voice = " +
+                 getDefaultVoiceSubId() + " sms = " + getDefaultSmsSubId());
+    }
+
+    protected void handleDataPreference(int nextActiveSubId) {
+        int userPrefDataSubId = getUserPrefDataSubIdFromDB();
+        int currentDataSubId = getDefaultDataSubId();
+
+        List<SubscriptionInfo> subInfoList =
+                getActiveSubscriptionInfoList(mContext.getOpPackageName());
+        if (subInfoList == null) {
+            return;
+        }
+        boolean userPrefSubValid = false;
+        for (SubscriptionInfo subInfo : subInfoList) {
+            if (subInfo.getSubscriptionId() == userPrefDataSubId) {
+                userPrefSubValid = true;
+            }
+        }
+        logd("havePrefSub = " + userPrefSubValid + " user pref subId = "
+                 + userPrefDataSubId + " current dds " + currentDataSubId
+                 + " next active subId " + nextActiveSubId);
+
+        // If earlier user selected DDS is now available, set that as DDS subId.
+        if (userPrefSubValid && isSubProvisioned(userPrefDataSubId) &&
+                 (currentDataSubId != userPrefDataSubId)) {
+            setDefaultDataSubId(userPrefDataSubId);
+        } else if (!isSubProvisioned(currentDataSubId)) {
+            setDefaultDataSubId(nextActiveSubId);
+        }
+
+    }
+
+    protected int getUiccProvisionStatus(int slotId) {
+        // FIXME use valid SIM provision value
+        return PROVISIONED; 
+    }
+
+    // This method returns true if subId and corresponding slotId is in valid
+    // range and the Uicc card corresponds to this slot is provisioned.
+    protected boolean isSubProvisioned(int subId) {
+        boolean isSubIdUsable = SubscriptionManager.isUsableSubIdValue(subId);
+
+        if (isSubIdUsable) {
+            int slotId = getSlotIndex(subId);
+            if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+                loge(" Invalid slotId " + slotId + " or subId = " + subId);
+                isSubIdUsable = false;
+            } else {
+                if (getUiccProvisionStatus(slotId) != PROVISIONED) {
+                    isSubIdUsable = false;
+                }
+                loge("isSubProvisioned, state = " + isSubIdUsable + " subId = " + subId);
+            }
+        }
+        return isSubIdUsable;
+    }
+
+    /* Returns User SMS Prompt property,  enabled or not */
+    public boolean isSmsPromptEnabled() {
+        boolean prompt = false;
+        int value = 0;
+        try {
+            value = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.MULTI_SIM_SMS_PROMPT);
+        } catch (SettingNotFoundException snfe) {
+            loge("Settings Exception Reading Dual Sim SMS Prompt Values");
+        }
+        prompt = (value == 0) ? false : true ;
+        if (VDBG) logd("SMS Prompt option:" + prompt);
+
+       return prompt;
+    }
+
+    /*Sets User SMS Prompt property,  enable or not */
+    public void setSmsPromptEnabled(boolean enabled) {
+        enforceModifyPhoneState("setSMSPromptEnabled");
+        int value = (enabled == false) ? 0 : 1;
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.MULTI_SIM_SMS_PROMPT, value);
+        logi("setSMSPromptOption to " + enabled);
+    }
+
+    protected boolean isNonSimAccountFound() {
+        final Iterator<PhoneAccountHandle> phoneAccounts =
+                mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+
+        while (phoneAccounts.hasNext()) {
+            final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+            final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+            if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) ==
+                            SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                logi("Other than SIM account found. ");
+                return true;
+            }
+        }
+        logi("Other than SIM account not found ");
+        return false;
+    }
+
+    protected PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
+        final Iterator<PhoneAccountHandle> phoneAccounts =
+                mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+
+        while (phoneAccounts.hasNext()) {
+            final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+            final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+            if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
+                return phoneAccountHandle;
+            }
+        }
+
+        return null;
+    }
+
+    protected int getUserPrefDataSubIdFromDB() {
+        return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+                SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
+    private void logd(String string) {
+        if (DBG) Rlog.d(LOG_TAG, string);
+    }
+
+    private void logi(String string) {
+        Rlog.i(LOG_TAG, string);
+    }
+
+    private void loge(String string) {
+        Rlog.e(LOG_TAG, string);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
new file mode 100644
index 0000000..6adc1ef
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.vendor.dataconnection;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class VendorDcTracker extends DcTracker {
+    private String LOG_TAG = "VendorDCT";
+    private static final boolean DBG = true;
+    private HashSet<String> mIccidSet = new HashSet<String>();
+    private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+
+    // Constructor
+    public VendorDcTracker(Phone phone, int transportType) {
+        super(phone, transportType);
+        mTransportType = transportType;
+        LOG_TAG = LOG_TAG + "-" +
+                ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) ? "C" : "I");
+        if (DBG) log(LOG_TAG + ".constructor");
+        fillIccIdSet();
+    }
+
+    protected boolean isRecordsLoaded() {
+        boolean recordsLoaded = false;
+
+        IccRecords r = mIccRecords.get();
+        if (r != null) {
+            recordsLoaded = r.getRecordsLoaded();
+        }
+
+        return recordsLoaded;
+    }
+
+    @Override
+    protected void onRecordsLoadedOrSubIdChanged() {
+        if (DBG) log("onRecordsLoaded: createAllApnList");
+        // Just support auto attach for WWAN only
+        if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+            mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
+                    .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
+        }
+
+        createAllApnList();
+        setDataProfilesAsNeeded();
+        // Send initial attach apn only if sim records are loaded
+        if (isRecordsLoaded()) {
+            setInitialAttachApn();
+        }
+        mPhone.notifyAllActiveDataConnections();
+        setupDataOnAllConnectableApns(Phone.REASON_SIM_LOADED, RetryFailures.ALWAYS);
+    }
+
+    @Override
+    protected boolean allowInitialAttachForOperator() {
+        IccRecords r = mIccRecords.get();
+        String iccId = (r != null) ? r.getIccId() : "";
+        if (iccId != null) {
+            Iterator<String> itr = mIccidSet.iterator();
+            while (itr.hasNext()) {
+                if (iccId.contains(itr.next())) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    // Support added to allow initial attach request with only default for a Carrier
+    protected void fillIccIdSet() {
+        mIccidSet.add("8991840");
+        mIccidSet.add("8991854");
+        mIccidSet.add("8991855");
+        mIccidSet.add("8991856");
+        mIccidSet.add("8991857");
+        mIccidSet.add("8991858");
+        mIccidSet.add("8991859");
+        mIccidSet.add("899186");
+        mIccidSet.add("8991870");
+        mIccidSet.add("8991871");
+        mIccidSet.add("8991872");
+        mIccidSet.add("8991873");
+        mIccidSet.add("8991874");
+    }
+
+    @Override
+    protected void log(String s) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+}