Merge "DO NOT MERGE - Merge pie-platform-release (PPRL.181205.001) into master"
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 4582404..40fe386 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -118,6 +118,10 @@
                 log("SET_METERED_APNS_ENABLED: " + mCarrierActionOnMeteredApnEnabled);
                 mMeteredApnEnabledLog.log("SET_METERED_APNS_ENABLED: "
                         + mCarrierActionOnMeteredApnEnabled);
+                int otaspState = (mCarrierActionOnMeteredApnEnabled)
+                        ? mPhone.getServiceStateTracker().getOtasp()
+                        : TelephonyManager.OTASP_SIM_UNPROVISIONED;
+                mPhone.notifyOtaspChanged(otaspState);
                 mMeteredApnEnableRegistrants.notifyRegistrants(
                         new AsyncResult(null, mCarrierActionOnMeteredApnEnabled, null));
                 break;
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index d582762..b6d2871 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -100,7 +100,7 @@
 
     /** Current cell tower information */
     @Nullable
-    private List<CellInfo> mCellInfo;
+    private List<CellInfo> mCellInfoList;
 
     /** Count of invalid cell info we've got so far. Will reset once we get a successful one */
     private int mFailCellInfoCount;
@@ -146,14 +146,14 @@
             case EVENT_UNSOL_CELL_INFO:
                 processCellInfo((AsyncResult) msg.obj);
                 // If the unsol happened to be useful, use it; otherwise, pretend it didn't happen.
-                if (mCellInfo != null && mCellInfo.size() > 0) requestNextCellInfo(true);
+                if (mCellInfoList != null && mCellInfoList.size() > 0) requestNextCellInfo(true);
                 break;
 
             case EVENT_RESPONSE_CELL_INFO:
                 processCellInfo((AsyncResult) msg.obj);
                 // If the cellInfo was non-empty then it's business as usual. Either way, this
                 // cell info was requested by us, so it's our trigger to schedule another one.
-                requestNextCellInfo(mCellInfo != null && mCellInfo.size() > 0);
+                requestNextCellInfo(mCellInfoList != null && mCellInfoList.size() > 0);
                 break;
 
             case EVENT_SERVICE_STATE_CHANGED:
@@ -209,10 +209,10 @@
     @Nullable
     private String getMccFromCellInfo() {
         String selectedMcc = null;
-        if (mCellInfo != null) {
+        if (mCellInfoList != null) {
             Map<String, Integer> countryCodeMap = new HashMap<>();
             int maxCount = 0;
-            for (CellInfo cellInfo : mCellInfo) {
+            for (CellInfo cellInfo : mCellInfoList) {
                 String mcc = null;
                 if (cellInfo instanceof CellInfoGsm) {
                     mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString();
@@ -281,13 +281,28 @@
 
     private void processCellInfo(AsyncResult ar) {
         if (ar == null || ar.exception != null) {
-            mCellInfo = null;
+            mCellInfoList = null;
             return;
         }
-        mCellInfo = (List<CellInfo>) ar.result;
-        String msg = "getCellInfo: cell info=" + mCellInfo;
+        List<CellInfo> cellInfoList = (List<CellInfo>) ar.result;
+        String msg = "getCellInfo: cell info=" + cellInfoList;
         if (DBG) log(msg);
-        mLocalLog.log(msg);
+        if (cellInfoList != null) {
+            // We only log when cell identity changes, otherwise the local log is flooded with cell
+            // info.
+            if (mCellInfoList == null || cellInfoList.size() != mCellInfoList.size()) {
+                mLocalLog.log(msg);
+            } else {
+                for (int i = 0; i < cellInfoList.size(); i++) {
+                    if (!Objects.equals(mCellInfoList.get(i).getCellIdentity(),
+                            cellInfoList.get(i).getCellIdentity())) {
+                        mLocalLog.log(msg);
+                        break;
+                    }
+                }
+            }
+        }
+        mCellInfoList = cellInfoList;
         updateLocale();
     }
 
@@ -355,7 +370,7 @@
         String msg = "Stopping LocaleTracker";
         if (DBG) log(msg);
         mLocalLog.log(msg);
-        mCellInfo = null;
+        mCellInfoList = null;
         resetCellInfoRetry();
     }
 
@@ -392,12 +407,11 @@
             countryIso = MccTable.countryCodeForMcc(mcc);
         }
 
-        String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso;
-        log(msg);
-        mLocalLog.log(msg);
+        log("updateLocale: mcc = " + mcc + ", country = " + countryIso);
         boolean countryChanged = false;
         if (!Objects.equals(countryIso, mCurrentCountryIso)) {
-            msg = "updateLocale: Change the current country to " + countryIso;
+            String msg = "updateLocale: Change the current country to \"" + countryIso
+                    + "\", mcc = " + mcc;
             log(msg);
             mLocalLog.log(msg);
             mCurrentCountryIso = countryIso;
@@ -447,7 +461,7 @@
         ipw.println("mIsTracking = " + mIsTracking);
         ipw.println("mOperatorNumeric = " + mOperatorNumeric);
         ipw.println("mSimState = " + mSimState);
-        ipw.println("mCellInfo = " + mCellInfo);
+        ipw.println("mCellInfoList = " + mCellInfoList);
         ipw.println("mCurrentCountryIso = " + mCurrentCountryIso);
         ipw.println("mFailCellInfoCount = " + mFailCellInfoCount);
         ipw.println("Local logs:");
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 26284c1..ca85e33 100755
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -783,14 +783,6 @@
     private void onSetUserDataEnabled(boolean enabled) {
         if (mDataEnabledSettings.isUserDataEnabled() != enabled) {
             mDataEnabledSettings.setUserDataEnabled(enabled);
-            if (!getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
-                if (enabled) {
-                    notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
-                } else {
-                    notifyOffApnsOfAvailability(Phone.REASON_DATA_DISABLED);
-                }
-            }
-
             mPhone.notifyUserMobileDataStateChanged(enabled);
 
             // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
@@ -2188,16 +2180,10 @@
             mDataEnabledSettings.setCarrierDataEnabled(enabled);
 
             if (!enabled) {
-                // Send otasp_sim_unprovisioned so that SuW is able to proceed and notify users
-                mPhone.notifyOtaspChanged(TelephonyManager.OTASP_SIM_UNPROVISIONED);
                 // Tear down all metered apns
                 cleanUpAllConnectionsInternal(true,
                         Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
             } else {
-                // Re-evaluate Otasp state
-                int otaspState = mPhone.getServiceStateTracker().getOtasp();
-                mPhone.notifyOtaspChanged(otaspState);
-
                 reevaluateDataConnections();
                 setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
             }
@@ -3188,7 +3174,7 @@
 
         // ORDER BY Telephony.Carriers._ID ("_id")
         Cursor cursor = mPhone.getContext().getContentResolver().query(
-                Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "filtered/subId/"
+                Uri.withAppendedPath(Telephony.Carriers.SIM_APN_LIST, "filtered/subId/"
                         + mPhone.getSubId()), null, null, null, Telephony.Carriers._ID);
 
         if (cursor != null) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index cb860d8..ff01405 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -128,9 +128,17 @@
     // considered sensetive information.
     private ArrayList<String> mCardStrings;
 
+    // This is the card ID of the default eUICC. It is set to the first ever seen eUICC
+    private int mDefaultEuiccCardId;
+
+    private static final int INVALID_CARD_ID = TelephonyManager.INVALID_CARD_ID;
+
     // SharedPreference key for saving the known card strings (ICCIDs and EIDs) ordered by card ID
     private static final String CARD_STRINGS = "card_strings";
 
+    // SharedPreferences key for saving the default euicc card ID
+    private static final String DEFAULT_CARD = "default_card";
+
     private static final Object mLock = new Object();
     private static UiccController mInstance;
     private static ArrayList<IccSlotStatus> sLastSlotStatus;
@@ -199,6 +207,7 @@
 
         mLauncher = new UiccStateChangedLauncher(c, this);
         mCardStrings = loadCardStrings();
+        mDefaultEuiccCardId = loadDefaultEuiccCardId();
     }
 
     private int getSlotIdFromPhoneId(int phoneId) {
@@ -570,10 +579,22 @@
 
     /**
      * Converts the card string (the ICCID/EID, formerly named card ID) to the public int cardId.
-     * Returns -1 if the card string does not map to a cardId.
+     * Returns INVALID_CARD_ID if the card string does not map to a cardId.
      */
     public int convertToPublicCardId(String cardString) {
-        return mCardStrings.indexOf(cardString);
+        int id = mCardStrings.indexOf(cardString);
+        if (id == -1) {
+            return INVALID_CARD_ID;
+        } else {
+            return id;
+        }
+    }
+
+    /**
+     * Get the card ID of the default eUICC.
+     */
+    public int getCardIdForDefaultEuicc() {
+        return mDefaultEuiccCardId;
     }
 
     private ArrayList<String> loadCardStrings() {
@@ -593,6 +614,19 @@
         editor.commit();
     }
 
+    private int loadDefaultEuiccCardId() {
+        return PreferenceManager.getDefaultSharedPreferences(mContext)
+                .getInt(DEFAULT_CARD, INVALID_CARD_ID);
+    }
+
+    private void setDefaultEuiccCardId(int cardId) {
+        mDefaultEuiccCardId = cardId;
+        SharedPreferences.Editor editor =
+                PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+        editor.putInt(DEFAULT_CARD, mDefaultEuiccCardId);
+        editor.commit();
+    }
+
     private synchronized void onGetSlotStatusDone(AsyncResult ar) {
         if (!mIsSlotStatusSupported) {
             if (VDBG) log("onGetSlotStatusDone: ignoring since mIsSlotStatusSupported is false");
@@ -655,6 +689,12 @@
             if (mUiccSlots[i].isEuicc()) {
                 String eid = iss.eid;
                 addCardId(eid);
+
+                // If default eUICC card ID is unset, set it to the card ID of the eUICC with the
+                // lowest slot index.
+                if (mDefaultEuiccCardId == INVALID_CARD_ID) {
+                    setDefaultEuiccCardId(convertToPublicCardId(eid));
+                }
             }
         }
 
@@ -801,6 +841,8 @@
         pw.flush();
         pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
         pw.println(" mUiccSlots: size=" + mUiccSlots.length);
+        pw.println(" mCardStrings=" + mCardStrings);
+        pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
         for (int i = 0; i < mUiccSlots.length; i++) {
             if (mUiccSlots[i] == null) {
                 pw.println("  mUiccSlots[" + i + "]=null");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 04e9305..12c87e0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -208,7 +208,9 @@
 
             if (uri.compareTo(Telephony.Carriers.CONTENT_URI) == 0
                     || uri.toString().startsWith(Uri.withAppendedPath(
-                            Telephony.Carriers.CONTENT_URI, "filtered").toString())) {
+                            Telephony.Carriers.CONTENT_URI, "filtered").toString())
+                    || uri.toString().startsWith(Uri.withAppendedPath(
+                            Telephony.Carriers.SIM_APN_LIST, "filtered").toString())) {
                 if (projection == null) {
 
                     logd("Query '" + FAKE_PLMN + "' APN settings");
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 4012e76..3b8b799 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -259,4 +259,28 @@
         // assert that the card ID was created
         assertEquals(0, mUiccControllerUT.convertToPublicCardId(iss.eid));
     }
+
+    @Test
+    public void testCardIdForDefaultEuicc() {
+        // Give UiccController a real context so it can use shared preferences
+        mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
+
+        // Mock out UiccSlots
+        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        doReturn(true).when(mMockSlot).isEuicc();
+
+        // simulate slot status loaded
+        IccSlotStatus iss = new IccSlotStatus();
+        iss.setSlotState(1 /* active */);
+        iss.eid = "ABADACB";
+        ArrayList<IccSlotStatus> status = new ArrayList<IccSlotStatus>();
+        status.add(iss);
+        AsyncResult ar = new AsyncResult(null, status, null);
+        Message msg = Message.obtain(mUiccControllerUT, EVENT_GET_SLOT_STATUS_DONE, ar);
+        mUiccControllerUT.handleMessage(msg);
+
+        // assert that the default cardId is the slot with the lowest slot index, even if inactive
+        assertEquals(mUiccControllerUT.convertToPublicCardId(iss.eid),
+                mUiccControllerUT.getCardIdForDefaultEuicc());
+    }
 }