Merge "[Settings] Replacing accessing of PhoneConstants$DataState"
diff --git a/src/com/android/settings/network/ApnSettings.java b/src/com/android/settings/network/ApnSettings.java
index 94d7bca..eb6c9b1 100755
--- a/src/com/android/settings/network/ApnSettings.java
+++ b/src/com/android/settings/network/ApnSettings.java
@@ -38,6 +38,8 @@
 import android.os.UserManager;
 import android.provider.Telephony;
 import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -52,8 +54,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceGroup;
 
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.settings.R;
 import com.android.settings.RestrictedSettingsFragment;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -107,6 +107,7 @@
     private static boolean mRestoreDefaultApnMode;
 
     private UserManager mUserManager;
+    private TelephonyManager mTelephonyManager;
     private RestoreApnUiHandler mRestoreApnUiHandler;
     private RestoreApnProcessHandler mRestoreApnProcessHandler;
     private HandlerThread mRestoreDefaultApnThread;
@@ -130,32 +131,35 @@
         super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
     }
 
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onPreciseDataConnectionStateChanged(
+                PreciseDataConnectionState dataConnectionState) {
+            if (dataConnectionState.getState() == TelephonyManager.DATA_CONNECTED) {
+                if (!mRestoreDefaultApnMode) {
+                    fillList();
+                } else {
+                    showDialog(DIALOG_RESTORE_DEFAULTAPN);
+                }
+            }
+        }
+    };
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(
-                    TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
-                PhoneConstants.DataState state = getMobileDataState(intent);
-                switch (state) {
-                case CONNECTED:
-                    if (!mRestoreDefaultApnMode) {
-                        fillList();
-                    } else {
-                        showDialog(DIALOG_RESTORE_DEFAULTAPN);
-                    }
-                    break;
-                }
-            } else if(intent.getAction().equals(
                     TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
                 if (!mRestoreDefaultApnMode) {
                     int extraSubId = intent.getIntExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID,
                             SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                    if (SubscriptionManager.isValidSubscriptionId(extraSubId) &&
-                            mPhoneId == SubscriptionManager.getPhoneId(extraSubId) &&
-                            extraSubId != mSubId) {
+                    if (SubscriptionManager.isValidSubscriptionId(extraSubId)
+                            && mPhoneId == SubscriptionUtil.getPhoneId(context, extraSubId)
+                            && extraSubId != mSubId) {
                         // subscription has changed
                         mSubId = extraSubId;
                         mSubscriptionInfo = getSubscriptionInfo(mSubId);
+                        restartPhoneStateListener(mSubId);
                     }
                     fillList();
                 }
@@ -163,13 +167,22 @@
         }
     };
 
-    private static PhoneConstants.DataState getMobileDataState(Intent intent) {
-        String str = intent.getStringExtra(PhoneConstants.STATE_KEY);
-        if (str != null) {
-            return Enum.valueOf(PhoneConstants.DataState.class, str);
-        } else {
-            return PhoneConstants.DataState.DISCONNECTED;
+    private void restartPhoneStateListener(int subId) {
+        if (mRestoreDefaultApnMode) {
+            return;
         }
+
+        final TelephonyManager updatedTelephonyManager =
+                mTelephonyManager.createForSubscriptionId(subId);
+
+        // restart monitoring when subscription has been changed
+        mTelephonyManager.listen(mPhoneStateListener,
+                PhoneStateListener.LISTEN_NONE);
+
+        mTelephonyManager = updatedTelephonyManager;
+
+        mTelephonyManager.listen(mPhoneStateListener,
+                PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE);
     }
 
     @Override
@@ -183,14 +196,14 @@
         final Activity activity = getActivity();
         mSubId = activity.getIntent().getIntExtra(SUB_ID,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        mPhoneId = SubscriptionManager.getPhoneId(mSubId);
+        mPhoneId = SubscriptionUtil.getPhoneId(activity, mSubId);
         mIntentFilter = new IntentFilter(
-                TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+                TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
 
         setIfOnlyAvailableForAdmins(true);
 
         mSubscriptionInfo = getSubscriptionInfo(mSubId);
+        mTelephonyManager = activity.getSystemService(TelephonyManager.class);
 
         CarrierConfigManager configManager = (CarrierConfigManager)
                 getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -235,6 +248,8 @@
 
         getActivity().registerReceiver(mReceiver, mIntentFilter);
 
+        restartPhoneStateListener(mSubId);
+
         if (!mRestoreDefaultApnMode) {
             fillList();
         }
@@ -249,6 +264,9 @@
         }
 
         getActivity().unregisterReceiver(mReceiver);
+
+        mTelephonyManager.listen(mPhoneStateListener,
+                PhoneStateListener.LISTEN_NONE);
     }
 
     @Override
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 9007c0b..f0c1583 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -16,15 +16,18 @@
 
 package com.android.settings.network;
 
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
 
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
 
 import android.content.Context;
+import android.os.ParcelUuid;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.UiccSlotInfo;
+import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -66,6 +69,12 @@
                 slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT;
     }
 
+    /**
+     * Get all of the subscriptions which is available to display to the user.
+     *
+     * @param context {@code Context}
+     * @return list of {@code SubscriptionInfo}
+     */
     public static List<SubscriptionInfo> getAvailableSubscriptions(Context context) {
         if (sAvailableResultsForTesting != null) {
             return sAvailableResultsForTesting;
@@ -73,12 +82,12 @@
         final SubscriptionManager subMgr = context.getSystemService(SubscriptionManager.class);
         final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
 
-        List<SubscriptionInfo> subscriptions =
+        final List<SubscriptionInfo> subscriptions =
                 new ArrayList<>(emptyIfNull(subMgr.getSelectableSubscriptionInfoList()));
 
         // Look for inactive but present physical SIMs that are missing from the selectable list.
         final List<UiccSlotInfo> missing = new ArrayList<>();
-        UiccSlotInfo[] slotsInfo =  telMgr.getUiccSlotsInfo();
+        final UiccSlotInfo[] slotsInfo = telMgr.getUiccSlotsInfo();
         for (int i = 0; slotsInfo != null && i < slotsInfo.length; i++) {
             final UiccSlotInfo slotInfo = slotsInfo[i];
             if (isInactiveInsertedPSim(slotInfo)) {
@@ -92,20 +101,164 @@
                 }
             }
         }
-        if (!missing.isEmpty()) {
-            for (SubscriptionInfo info : subMgr.getAllSubscriptionInfoList()) {
-                for (UiccSlotInfo slotInfo : missing) {
-                    if (info.getSimSlotIndex() == slotInfo.getLogicalSlotIdx() &&
-                    info.getCardString().equals(slotInfo.getCardId())) {
-                        subscriptions.add(info);
-                        break;
-                    }
+        if (missing.isEmpty()) {
+            return subscriptions;
+        }
+        for (SubscriptionInfo info : subMgr.getAllSubscriptionInfoList()) {
+            for (UiccSlotInfo slotInfo : missing) {
+                if (info.getSimSlotIndex() == slotInfo.getLogicalSlotIdx()
+                        && info.getCardString().equals(slotInfo.getCardId())) {
+                    subscriptions.add(info);
+                    break;
                 }
             }
         }
         return subscriptions;
     }
 
+    /**
+     * Get subscription which is available to be displayed to the user
+     * per subscription id.
+     *
+     * @param context {@code Context}
+     * @param subscriptionManager The ProxySubscriptionManager for accessing subcription
+     *         information
+     * @param subId The id of subscription to be retrieved
+     * @return {@code SubscriptionInfo} based on the given subscription id. Null of subscription
+     *         is invalid or not allowed to be displayed to the user.
+     */
+    public static SubscriptionInfo getAvailableSubscription(Context context,
+            ProxySubscriptionManager subscriptionManager, int subId) {
+        final SubscriptionInfo subInfo = subscriptionManager.getAccessibleSubscriptionInfo(subId);
+        if (subInfo == null) {
+            return null;
+        }
+
+        final ParcelUuid groupUuid = subInfo.getGroupUuid();
+
+        if (groupUuid != null) {
+            if (isPrimarySubscriptionWithinSameUuid(getUiccSlotsInfo(context), groupUuid,
+                    subscriptionManager.getAccessibleSubscriptionsInfo(), subId)) {
+                return subInfo;
+            }
+            return null;
+        }
+
+        if (subInfo.isEmbedded()) {
+            return subInfo;
+        }
+
+        // Look for physical SIM which presented in slots no mater active or not.
+        final UiccSlotInfo[] slotsInfo = getUiccSlotsInfo(context);
+        if (slotsInfo == null) {
+            return null;
+        }
+        for (UiccSlotInfo slotInfo : slotsInfo) {
+            if ((!slotInfo.getIsEuicc())
+                    && (slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT)
+                    && (slotInfo.getLogicalSlotIdx() == subInfo.getSimSlotIndex())
+                    && TextUtils.equals(slotInfo.getCardId(), subInfo.getCardString())) {
+                return subInfo;
+            }
+        }
+        return null;
+    }
+
+    private static UiccSlotInfo [] getUiccSlotsInfo(Context context) {
+        final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
+        return telMgr.getUiccSlotsInfo();
+    }
+
+    private static boolean isPrimarySubscriptionWithinSameUuid(UiccSlotInfo[] slotsInfo,
+            ParcelUuid groupUuid, List<SubscriptionInfo> subscriptions, int subId) {
+        // only interested in subscriptions with this group UUID
+        final ArrayList<SubscriptionInfo> physicalSubInfoList =
+                new ArrayList<SubscriptionInfo>();
+        final ArrayList<SubscriptionInfo> nonOpportunisticSubInfoList =
+                new ArrayList<SubscriptionInfo>();
+        final ArrayList<SubscriptionInfo> activeSlotSubInfoList =
+                new ArrayList<SubscriptionInfo>();
+        final ArrayList<SubscriptionInfo> inactiveSlotSubInfoList =
+                new ArrayList<SubscriptionInfo>();
+        for (SubscriptionInfo subInfo : subscriptions) {
+            if (groupUuid.equals(subInfo.getGroupUuid())) {
+                if (!subInfo.isEmbedded()) {
+                    physicalSubInfoList.add(subInfo);
+                } else  {
+                    if (!subInfo.isOpportunistic()) {
+                        nonOpportunisticSubInfoList.add(subInfo);
+                    }
+                    if (subInfo.getSimSlotIndex()
+                            != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                        activeSlotSubInfoList.add(subInfo);
+                    } else {
+                        inactiveSlotSubInfoList.add(subInfo);
+                    }
+                }
+            }
+        }
+
+        // find any physical SIM which is currently inserted within logical slot
+        // and which is our target subscription
+        if ((slotsInfo != null) && (physicalSubInfoList.size() > 0)) {
+            final SubscriptionInfo subInfo = searchForSubscriptionId(physicalSubInfoList, subId);
+            if (subInfo == null) {
+                return false;
+            }
+            // verify if subscription is inserted within slot
+            for (UiccSlotInfo slotInfo : slotsInfo) {
+                if ((slotInfo != null) && (!slotInfo.getIsEuicc())
+                        && (slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT)
+                        && (slotInfo.getLogicalSlotIdx() == subInfo.getSimSlotIndex())
+                        && TextUtils.equals(slotInfo.getCardId(), subInfo.getCardString())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        // When all of the eSIM profiles are opprtunistic and no physical SIM,
+        // first opportunistic subscriptions with same group UUID can be primary.
+        if (nonOpportunisticSubInfoList.size() <= 0) {
+            if (physicalSubInfoList.size() > 0) {
+                return false;
+            }
+            if (activeSlotSubInfoList.size() > 0) {
+                return (activeSlotSubInfoList.get(0).getSubscriptionId() == subId);
+            }
+            return (inactiveSlotSubInfoList.get(0).getSubscriptionId() == subId);
+        }
+
+        // Allow non-opportunistic + active eSIM subscription as primary
+        int numberOfActiveNonOpportunisticSubs = 0;
+        boolean isTargetNonOpportunistic = false;
+        for (SubscriptionInfo subInfo : nonOpportunisticSubInfoList) {
+            final boolean isTargetSubInfo = (subInfo.getSubscriptionId() == subId);
+            if (subInfo.getSimSlotIndex() != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                if (isTargetSubInfo) {
+                    return true;
+                }
+                numberOfActiveNonOpportunisticSubs++;
+            } else {
+                isTargetNonOpportunistic |= isTargetSubInfo;
+            }
+        }
+        if (numberOfActiveNonOpportunisticSubs > 0) {
+            return false;
+        }
+        return isTargetNonOpportunistic;
+    }
+
+    private static SubscriptionInfo searchForSubscriptionId(List<SubscriptionInfo> subInfoList,
+            int subscriptionId) {
+        for (SubscriptionInfo subInfo : subInfoList) {
+            if (subInfo.getSubscriptionId() == subscriptionId) {
+                return subInfo;
+            }
+        }
+        return null;
+    }
+
     public static String getDisplayName(SubscriptionInfo info) {
         final CharSequence name = info.getDisplayName();
         if (name != null) {
@@ -113,4 +266,26 @@
         }
         return "";
     }
+
+    /**
+     * Whether Settings should show a "Use SIM" toggle in pSIM detailed page.
+     */
+    public static boolean showToggleForPhysicalSim(SubscriptionManager subMgr) {
+        return subMgr.canDisablePhysicalSubscription();
+    }
+
+    /**
+     * Get phoneId or logical slot index for a subId if active, or INVALID_PHONE_INDEX if inactive.
+     */
+    public static int getPhoneId(Context context, int subId) {
+        final SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+        if (subManager == null) {
+            return INVALID_SIM_SLOT_INDEX;
+        }
+        final SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId);
+        if (info == null) {
+            return INVALID_SIM_SLOT_INDEX;
+        }
+        return info.getSimSlotIndex();
+    }
 }