Snap for 7550844 from 321187203cbb274069177b9b7fba8286f53a8d23 to mainline-conscrypt-release

Change-Id: Idc51f01be7c3b90082022cc917888dba1106c396
diff --git a/Android.bp b/Android.bp
index def7cde..3b65737 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,22 @@
+package {
+    default_applicable_licenses: [
+        "packages_providers_TelephonyProvider_license",
+    ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "packages_providers_TelephonyProvider_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 android_app {
     name: "TelephonyProvider",
     privileged: true,
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/assets/README b/assets/README
index ad6a29c..43b0965 100644
--- a/assets/README
+++ b/assets/README
@@ -10,3 +10,50 @@
 DO NOT MANUALLY EDIT THIS FILE
 
 This file defines carrier id and should be single versioned.
+
+===== How to test carrier id locally =====
+
+If you want to make change locally during testing, currently there are two ways:
+
+1. Modify carrierIdentification.db database by SQL command
+
+For example (Insert MCCMNC '12345' and gid1 'test' to carrier id 20000):
+```
+$ adb shell
+device:/ $ su
+device:/ # DB='/data/user_de/0/com.android.providers.telephony/databases/carrierIdentification.db'
+device:/ # sqlite3 $DB "INSERT INTO carrier_id(mccmnc, gid1, carrier_id, carrier_name) VALUES (12345, 'test', 20000, 'test_carrier')"
+device:/ # reboot
+```
+
+2. Override carrier_list.pb
+
+- Modify carrier_list.textpb directly (Note: You should also bump the version
+  number to let TelephonyProvider reload the carrier_list.pb)
+- Generate class file by using the carrier id proto(TelephonyProvider/proto/src/carrierId.proto)
+  (See https://developers.google.com/protocol-buffers/docs/overview#generating)
+- Create a converter by using TextFormat tool to convert textpb to pb
+  (Text Format: https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/TextFormat)
+- Rename file to carrier_list_test.pb and push the output file to
+    /data/user_de/0/com.android.providers.telephony/files/carrier_list_test.pb
+- Reboot the device
+
+Converter example:
+```
+#!/usr/bin/env python3
+from google.protobuf import text_format
+
+# Generated by: protoc -I=./ --python_out=./ ./carrierId.proto
+from carrierId_pb2 import CarrierList
+
+def main():
+  with open("carrier_list.textpb", "r") as rd:
+    carrierList = CarrierList()
+    text_format.Merge(rd.read(), carrierList)
+
+    with open("carrier_list.pb", "wb") as wf:
+      wf.write(carrierList.SerializeToString())
+
+if __name__ == '__main__':
+  main()
+```
diff --git a/assets/latest_carrier_id/carrier_list.pb b/assets/latest_carrier_id/carrier_list.pb
index 44a7056..d038016 100644
--- a/assets/latest_carrier_id/carrier_list.pb
+++ b/assets/latest_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/latest_carrier_id/carrier_list.textpb b/assets/latest_carrier_id/carrier_list.textpb
index a5d8050..62398c0 100644
--- a/assets/latest_carrier_id/carrier_list.textpb
+++ b/assets/latest_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/assets/sdk28_carrier_id/carrier_list.pb b/assets/sdk28_carrier_id/carrier_list.pb
index 700eb89..91d6640 100644
--- a/assets/sdk28_carrier_id/carrier_list.pb
+++ b/assets/sdk28_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/sdk28_carrier_id/carrier_list.textpb b/assets/sdk28_carrier_id/carrier_list.textpb
index 619d989..0cd3a99 100644
--- a/assets/sdk28_carrier_id/carrier_list.textpb
+++ b/assets/sdk28_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/assets/sdk29_carrier_id/carrier_list.pb b/assets/sdk29_carrier_id/carrier_list.pb
index 3519d2c..5e78f39 100644
--- a/assets/sdk29_carrier_id/carrier_list.pb
+++ b/assets/sdk29_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/sdk29_carrier_id/carrier_list.textpb b/assets/sdk29_carrier_id/carrier_list.textpb
index b1c6825..6c1420c 100644
--- a/assets/sdk29_carrier_id/carrier_list.textpb
+++ b/assets/sdk29_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/assets/sdk30_carrier_id/carrier_list.pb b/assets/sdk30_carrier_id/carrier_list.pb
new file mode 100644
index 0000000..946634d
--- /dev/null
+++ b/assets/sdk30_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/sdk30_carrier_id/carrier_list.textpb b/assets/sdk30_carrier_id/carrier_list.textpb
new file mode 100644
index 0000000..e301ad0
--- /dev/null
+++ b/assets/sdk30_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/proto/Android.bp b/proto/Android.bp
index 35dcc98..810e346 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -12,6 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_providers_TelephonyProvider_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: [
+        "packages_providers_TelephonyProvider_license",
+    ],
+}
+
 filegroup {
     name: "telephonyprovider-proto-sources",
     srcs: ["src/**/*.proto"],
diff --git a/res/values/config.xml b/res/values/config.xml
index 23c08b8..efe772f 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -14,4 +14,12 @@
          If this value is empty or unparsable, we will apply APNs from the APN
          conf xml file.  -->
     <string name="apn_source_service" translatable="false"></string>
+
+    <!-- Countries, in iso country code format, where WFC entitlement or
+         other user action is required before user can turn on WFC setting,
+         hence WFC setting should not be restored automatically. -->
+    <string-array name="wfc_restore_blocked_countries" translatable="false">
+        <item>us</item>
+        <item>ca</item>
+    </string-array>
 </resources>
diff --git a/src/com/android/providers/telephony/CarrierIdProvider.java b/src/com/android/providers/telephony/CarrierIdProvider.java
index a3c299f..3e751aa 100644
--- a/src/com/android/providers/telephony/CarrierIdProvider.java
+++ b/src/com/android/providers/telephony/CarrierIdProvider.java
@@ -83,6 +83,9 @@
     private static final int VERSION_BITMASK = 0x00FFFFFF;
     private static final String OTA_UPDATED_PB_PATH = "misc/carrierid/" + ASSETS_PB_FILE;
     private static final String PREF_FILE = CarrierIdProvider.class.getSimpleName();
+    // For testing purposes only.
+    private static final String OVERRIDE_PB_PATH =
+            "/data/user_de/0/com.android.providers.telephony/files/carrier_list_test.pb";
 
     private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
@@ -527,9 +530,14 @@
         CarrierIdProto.CarrierList assets = null;
         CarrierIdProto.CarrierList ota = null;
         InputStream is = null;
+        File testFile = new File(OVERRIDE_PB_PATH);
 
         try {
-            is = getContext().getAssets().open(ASSETS_PB_FILE);
+            if (Build.IS_DEBUGGABLE && testFile.exists()) {
+                is = new FileInputStream(testFile);
+            } else {
+                is = getContext().getAssets().open(ASSETS_PB_FILE);
+            }
             assets = CarrierIdProto.CarrierList.parseFrom(readInputStreamToByteArray(is));
         } catch (IOException ex) {
             Log.e(TAG, "read carrier list from assets pb failure: " + ex);
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 068f209..a9494fb 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -526,6 +526,15 @@
     @Override
     public void onCreate(SQLiteDatabase db) {
         localLog("onCreate: Creating all SMS-MMS tables.");
+
+        createMmsTables(db);
+        createSmsTables(db);
+        createCommonTables(db);
+        createCommonTriggers(db);
+        createMmsTriggers(db);
+        createWordsTables(db);
+        createIndices(db);
+
         // if FBE is not supported, or if this onCreate is for CE partition database
         if (!StorageManager.isFileEncryptedNativeOrEmulated()
                 || (mContext != null && mContext.isCredentialProtectedStorage())) {
@@ -546,13 +555,6 @@
 
             mContext.sendBroadcast(intent);
         }
-        createMmsTables(db);
-        createSmsTables(db);
-        createCommonTables(db);
-        createCommonTriggers(db);
-        createMmsTriggers(db);
-        createWordsTables(db);
-        createIndices(db);
     }
 
     private static void localLog(String logMsg) {
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index f5aeadc..8e01bcc 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -305,7 +305,6 @@
     private SQLiteOpenHelper mOpenHelper;
 
     private boolean mUseStrictPhoneNumberComparation;
-    private int mMinMatch;
 
     private static final String METHOD_IS_RESTORING = "is_restoring";
     private static final String IS_RESTORING_KEY = "restoring";
@@ -317,9 +316,6 @@
         mUseStrictPhoneNumberComparation =
             getContext().getResources().getBoolean(
                     com.android.internal.R.bool.config_use_strict_phone_number_comparation);
-        mMinMatch =
-            getContext().getResources().getInteger(
-                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
         TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
         return true;
     }
@@ -551,12 +547,15 @@
         String selection = "address=?";
         String[] selectionArgs;
         long retVal = -1L;
+        int minMatch =
+            getContext().getResources().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
 
         if (!isPhoneNumber) {
             selectionArgs = new String[] { refinedAddress };
         } else {
             selection += " OR PHONE_NUMBERS_EQUAL(address, ?, " +
-                        (mUseStrictPhoneNumberComparation ? "1)" : "0, " + mMinMatch + ")");
+                        (mUseStrictPhoneNumberComparation ? "1)" : "0, " + minMatch + ")");
             selectionArgs = new String[] { refinedAddress, refinedAddress };
         }
 
@@ -1011,19 +1010,22 @@
      *   FROM pdu, (SELECT msg_id AS address_msg_id
      *              FROM addr
      *              WHERE (address='<phoneNumber>' OR
-     *              PHONE_NUMBERS_EQUAL(addr.address, '<phoneNumber>', 1/0, none/mMinMatch)))
+     *              PHONE_NUMBERS_EQUAL(addr.address, '<phoneNumber>', 1/0, none/minMatch)))
      *             AS matching_addresses
      *   WHERE pdu._id = matching_addresses.address_msg_id
      * UNION
      * SELECT ...
      *   FROM sms
      *   WHERE (address='<phoneNumber>' OR
-     *          PHONE_NUMBERS_EQUAL(sms.address, '<phoneNumber>', 1/0, none/mMinMatch));
+     *          PHONE_NUMBERS_EQUAL(sms.address, '<phoneNumber>', 1/0, none/minMatch));
      */
     private Cursor getMessagesByPhoneNumber(
             String phoneNumber, String[] projection, String selection,
             String sortOrder, String smsTable, String pduTable) {
         String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(phoneNumber);
+        int minMatch =
+            getContext().getResources().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
         String finalMmsSelection =
                 concatSelections(
                         selection,
@@ -1033,7 +1035,7 @@
                         selection,
                         "(address=" + escapedPhoneNumber + " OR PHONE_NUMBERS_EQUAL(address, " +
                         escapedPhoneNumber +
-                        (mUseStrictPhoneNumberComparation ? ", 1))" : ", 0, " + mMinMatch + "))"));
+                        (mUseStrictPhoneNumberComparation ? ", 1))" : ", 0, " + minMatch + "))"));
         SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
         SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
 
@@ -1045,7 +1047,7 @@
                 "FROM addr WHERE (address=" + escapedPhoneNumber +
                 " OR PHONE_NUMBERS_EQUAL(addr.address, " +
                 escapedPhoneNumber +
-                (mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0, " + mMinMatch + "))) ") +
+                (mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0, " + minMatch + "))) ") +
                 "AS matching_addresses");
         smsQueryBuilder.setTables(smsTable);
 
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 81f732b..d378c64 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -84,7 +84,7 @@
         "body",                         // getDisplayMessageBody
         "date",                         // getTimestampMillis
         "status",                       // getStatusOnIcc
-        "index_on_icc",                 // getIndexOnIcc
+        "index_on_icc",                 // getIndexOnIcc (1-based index)
         "is_status_report",             // isStatusReportMessage
         "transport_type",               // Always "sms".
         "type",                         // depend on getStatusOnIcc
@@ -353,20 +353,42 @@
                 type = Sms.MESSAGE_TYPE_OUTBOX;
                 break;
         }
+
+        String address = (type == Sms.MESSAGE_TYPE_INBOX)
+                ? message.getDisplayOriginatingAddress()
+                : message.getRecipientAddress();
+
+        int index = message.getIndexOnIcc();
+        if (address == null) {
+            // The status byte of an EF_SMS record may not be correct. try to read other address
+            // type again.
+            Log.e(TAG, "convertIccToSms: EF_SMS(" + index + ")=> address=null, type=" + type
+                    + ", status=" + statusOnIcc + "(may not be correct). fallback to other type.");
+            address = (type == Sms.MESSAGE_TYPE_INBOX)
+                    ? message.getRecipientAddress()
+                    : message.getDisplayOriginatingAddress();
+
+            if (address != null) {
+                // Rely on actual PDU(address) to set type again.
+                type = (type == Sms.MESSAGE_TYPE_INBOX)
+                        ? Sms.MESSAGE_TYPE_SENT
+                        : Sms.MESSAGE_TYPE_INBOX;
+                Log.d(TAG, "convertIccToSms: new type=" + type + ", address=xxxxxx");
+            } else {
+                Log.e(TAG, "convertIccToSms: no change");
+            }
+        }
+
         // N.B.: These calls must appear in the same order as the
         // columns appear in ICC_COLUMNS.
         Object[] row = new Object[13];
         row[0] = message.getServiceCenterAddress();
-        row[1] =
-                (type == Sms.MESSAGE_TYPE_INBOX)
-                        ? message.getDisplayOriginatingAddress()
-                        : message.getRecipientAddress();
-
+        row[1] = address;
         row[2] = String.valueOf(message.getMessageClass());
         row[3] = message.getDisplayMessageBody();
         row[4] = message.getTimestampMillis();
         row[5] = statusOnIcc;
-        row[6] = message.getIndexOnIcc();
+        row[6] = index;
         row[7] = message.isStatusReportMessage();
         row[8] = "sms";
         row[9] = type;
@@ -380,7 +402,7 @@
      * Gets single message from the ICC for a subscription ID.
      *
      * @param subId the subscription ID.
-     * @param messageIndex the message index of the messaage in the ICC.
+     * @param messageIndex the message index of the messaage in the ICC (1-based index).
      * @return a cursor containing just one message from the ICC for the subscription ID.
      */
     private Cursor getSingleMessageFromIcc(int subId, int messageIndex) {
@@ -393,19 +415,24 @@
         // Use phone app permissions to avoid UID mismatch in AppOpsManager.noteOp() call.
         long token = Binder.clearCallingIdentity();
         try {
+            // getMessagesFromIcc() returns a zero-based list of valid messages in the ICC.
             messages = smsManager.getMessagesFromIcc();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
 
-        final SmsMessage message = messages.get(messageIndex);
-        if (message == null) {
-            throw new IllegalArgumentException(
-                    "No message in index " + messageIndex + " for subId " + subId);
+        final int count = messages.size();
+        for (int i = 0; i < count; i++) {
+            SmsMessage message = messages.get(i);
+            if (message != null && message.getIndexOnIcc() == messageIndex) {
+                MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, 1);
+                cursor.addRow(convertIccToSms(message, 0));
+                return cursor;
+            }
         }
-        MatrixCursor cursor = new MatrixCursor(ICC_COLUMNS, 1);
-        cursor.addRow(convertIccToSms(message, 0));
-        return cursor;
+
+        throw new IllegalArgumentException(
+                "No message in index " + messageIndex + " for subId " + subId);
     }
 
     /**
@@ -424,6 +451,7 @@
         // Use phone app permissions to avoid UID mismatch in AppOpsManager.noteOp() call
         long token = Binder.clearCallingIdentity();
         try {
+            // getMessagesFromIcc() returns a zero-based list of valid messages in the ICC.
             messages = smsManager.getMessagesFromIcc();
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -882,31 +910,62 @@
                 count = db.delete("sr_pending", where, whereArgs);
                 break;
 
+            case SMS_ALL_ICC:
+            case SMS_ALL_ICC_SUBID:
+                {
+                    int subId;
+                    int deletedCnt;
+                    if (match == SMS_ALL_ICC) {
+                        subId = SmsManager.getDefaultSmsSubscriptionId();
+                    } else {
+                        try {
+                            subId = Integer.parseInt(url.getPathSegments().get(1));
+                        } catch (NumberFormatException e) {
+                            throw new IllegalArgumentException("Wrong path segements, uri= " + url);
+                        }
+                    }
+                    deletedCnt = deleteAllMessagesFromIcc(subId);
+                    // Notify changes even failure case since there might be some changes should be
+                    // known.
+                    getContext()
+                            .getContentResolver()
+                            .notifyChange(
+                                    match == SMS_ALL_ICC ? ICC_URI : ICC_SUBID_URI,
+                                    null,
+                                    true,
+                                    UserHandle.USER_ALL);
+                    return deletedCnt;
+                }
+
             case SMS_ICC:
             case SMS_ICC_SUBID:
-                int subId;
-                int messageIndex;
-                boolean success;
-                try {
-                    if (match == SMS_ICC) {
-                        subId = SmsManager.getDefaultSmsSubscriptionId();
-                        messageIndex = Integer.parseInt(url.getPathSegments().get(1));
-                    } else {
-                        subId = Integer.parseInt(url.getPathSegments().get(1));
-                        messageIndex = Integer.parseInt(url.getPathSegments().get(2));
+                {
+                    int subId;
+                    int messageIndex;
+                    boolean success;
+                    try {
+                        if (match == SMS_ICC) {
+                            subId = SmsManager.getDefaultSmsSubscriptionId();
+                            messageIndex = Integer.parseInt(url.getPathSegments().get(1));
+                        } else {
+                            subId = Integer.parseInt(url.getPathSegments().get(1));
+                            messageIndex = Integer.parseInt(url.getPathSegments().get(2));
+                        }
+                    } catch (NumberFormatException e) {
+                        throw new IllegalArgumentException("Wrong path segements, uri= " + url);
                     }
-                } catch (NumberFormatException e) {
-                    throw new IllegalArgumentException("Wrong path segements, uri= " + url);
+                    success = deleteMessageFromIcc(subId, messageIndex);
+                    // Notify changes even failure case since there might be some changes should be
+                    // known.
+                    getContext()
+                            .getContentResolver()
+                            .notifyChange(
+                                    match == SMS_ICC ? ICC_URI : ICC_SUBID_URI,
+                                    null,
+                                    true,
+                                    UserHandle.USER_ALL);
+                    return success ? 1 : 0; // return deleted count
                 }
-                success = deleteMessageFromIcc(subId, messageIndex);
-                // Notify changes even failure case since there might be some changes should be
-                // known.
-                getContext().getContentResolver().notifyChange(
-                        match == SMS_ICC ? ICC_URI : ICC_SUBID_URI,
-                        null,
-                        true,
-                        UserHandle.USER_ALL);
-                return success ? 1 : 0; // return deleted count
 
             default:
                 throw new IllegalArgumentException("Unknown URL");
@@ -922,7 +981,7 @@
      * Deletes the message at index from the ICC for a subscription ID.
      *
      * @param subId the subscription ID.
-     * @param messageIndex the message index of the message in the ICC.
+     * @param messageIndex the message index of the message in the ICC (1-based index).
      * @return true for succeess. Otherwise false.
      */
     private boolean deleteMessageFromIcc(int subId, int messageIndex) {
@@ -940,6 +999,38 @@
         }
     }
 
+    /**
+     * Deletes all the messages from the ICC for a subscription ID.
+     *
+     * @param subId the subscription ID.
+     * @return return deleted messaegs count.
+     */
+    private int deleteAllMessagesFromIcc(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription ID " + subId);
+        }
+        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+
+        // Use phone app permissions to avoid UID mismatch in AppOpsManager.noteOp() call.
+        long token = Binder.clearCallingIdentity();
+        try {
+            int deletedCnt = 0;
+            int maxIndex = smsManager.getSmsCapacityOnIcc();
+            // messageIndex is 1-based index of the message in the ICC.
+            for (int messageIndex = 1; messageIndex <= maxIndex; messageIndex++) {
+                if (smsManager.deleteMessageFromIcc(messageIndex)) {
+                    deletedCnt++;
+                } else {
+                    Log.e(TAG, "Fail to delete SMS at index " + messageIndex
+                            + " for subId " + subId);
+                }
+            }
+            return deletedCnt;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
         final int callerUid = Binder.getCallingUid();
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index 5c764c8..34fed99 100644
--- a/src/com/android/providers/telephony/TelephonyBackupAgent.java
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
+import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
 import android.provider.Telephony;
 import android.telephony.PhoneNumberUtils;
@@ -50,6 +51,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneFactory;
 
 import com.google.android.mms.ContentType;
 import com.google.android.mms.pdu.CharacterSets;
@@ -114,6 +116,15 @@
     private static final boolean DEBUG = false;
     private static volatile boolean sIsRestoring;
 
+    // SharedPreferences keys
+    private static final String NUM_SMS_RESTORED = "num_sms_restored";
+    private static final String NUM_SMS_EXCEPTIONS = "num_sms_exceptions";
+    private static final String NUM_SMS_FILES_STORED = "num_sms_files_restored";
+    private static final String NUM_SMS_FILES_WITH_EXCEPTIONS = "num_sms_files_with_exceptions";
+    private static final String NUM_MMS_RESTORED = "num_mms_restored";
+    private static final String NUM_MMS_EXCEPTIONS = "num_mms_exceptions";
+    private static final String NUM_MMS_FILES_STORED = "num_mms_files_restored";
+    private static final String NUM_MMS_FILES_WITH_EXCEPTIONS = "num_mms_files_with_exceptions";
 
     // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
     private static final int DEFAULT_DURATION = 5000; //ms
@@ -316,7 +327,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-
+        Log.d(TAG, "onCreate");
         final SubscriptionManager subscriptionManager = SubscriptionManager.from(this);
         if (subscriptionManager != null) {
             final List<SubscriptionInfo> subInfo =
@@ -505,6 +516,28 @@
 
     public static class DeferredSmsMmsRestoreService extends IntentService {
         private static final String TAG = "DeferredSmsMmsRestoreService";
+        private static boolean sSharedPrefsAddedToLocalLogs = false;
+
+        public static void addAllSharedPrefToLocalLog(Context context) {
+            if (sSharedPrefsAddedToLocalLogs) return;
+            localLog("addAllSharedPrefToLocalLog");
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+            Map<String, ?> allPref = sp.getAll();
+            if (allPref.keySet() == null || allPref.keySet().size() == 0) return;
+            for (String key : allPref.keySet()) {
+                try {
+                    localLog(key + ":" + allPref.get(key).toString());
+                } catch (Exception e) {
+                    localLog("Skipping over key " + key + " due to exception " + e);
+                }
+            }
+            sSharedPrefsAddedToLocalLogs = true;
+        }
+
+        public static void localLog(String logMsg) {
+            Log.d(TAG, logMsg);
+            PhoneFactory.localLog(TAG, logMsg);
+        }
 
         private final Comparator<File> mFileComparator = new Comparator<File>() {
             @Override
@@ -515,6 +548,7 @@
 
         public DeferredSmsMmsRestoreService() {
             super(TAG);
+            Log.d(TAG, "DeferredSmsMmsRestoreService");
             setIntentRedelivery(true);
         }
 
@@ -523,6 +557,7 @@
 
         @Override
         protected void onHandleIntent(Intent intent) {
+            Log.d(TAG, "onHandleIntent");
             try {
                 mWakeLock.acquire();
                 sIsRestoring = true;
@@ -545,6 +580,7 @@
                     } catch (Exception e) {
                         // Either IOException or RuntimeException.
                         Log.e(TAG, "onHandleIntent", e);
+                        localLog("onHandleIntent: Exception " + e);
                     } finally {
                         file.delete();
                     }
@@ -552,11 +588,12 @@
                 if (didRestore) {
                   // Tell the default sms app to do a full sync now that the messages have been
                   // restored.
-                  Log.d(TAG, "onHandleIntent done - notifying default sms app");
+                  localLog("onHandleIntent: done - notifying default sms app");
                   ProviderUtil.notifyIfNotDefaultSmsApp(null /*uri*/, null /*calling package*/,
                       this);
                 }
            } finally {
+                addAllSharedPrefToLocalLog(this);
                 sIsRestoring = false;
                 mWakeLock.release();
             }
@@ -565,6 +602,12 @@
         @Override
         public void onCreate() {
             super.onCreate();
+            Log.d(TAG, "onCreate");
+            try {
+                PhoneFactory.addLocalLog(TAG, 32);
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
             mTelephonyBackupAgent = new TelephonyBackupAgent();
             mTelephonyBackupAgent.attach(this);
             mTelephonyBackupAgent.onCreate();
@@ -583,8 +626,15 @@
         }
 
         static void startIfFilesExist(Context context) {
+            try {
+                PhoneFactory.addLocalLog(TAG, 32);
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
             File[] files = getFilesToRestore(context);
             if (files == null || files.length == 0) {
+                Log.d(TAG, "startIfFilesExist: no files to restore");
+                addAllSharedPrefToLocalLog(context);
                 return;
             }
             context.startService(new Intent(context, DeferredSmsMmsRestoreService.class));
@@ -618,7 +668,7 @@
                 Log.d(TAG, "Restoring text MMS");
                 putMmsMessagesToProvider(jsonReader);
             } else {
-                Log.e(TAG, "Unknown file to restore:" + fileName);
+                DeferredSmsMmsRestoreService.localLog("Unknown file to restore:" + fileName);
             }
         }
     }
@@ -627,16 +677,23 @@
     void putSmsMessagesToProvider(JsonReader jsonReader) throws IOException {
         jsonReader.beginArray();
         int msgCount = 0;
+        int numExceptions = 0;
         final int bulkInsertSize = mMaxMsgPerFile;
         ContentValues[] values = new ContentValues[bulkInsertSize];
         while (jsonReader.hasNext()) {
             ContentValues cv = readSmsValuesFromReader(jsonReader);
-            if (doesSmsExist(cv)) {
-                continue;
-            }
-            values[(msgCount++) % bulkInsertSize] = cv;
-            if (msgCount % bulkInsertSize == 0) {
-                mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
+            try {
+                if (mSmsProviderQuery.doesSmsExist(cv)) {
+                    continue;
+                }
+                values[(msgCount++) % bulkInsertSize] = cv;
+                if (msgCount % bulkInsertSize == 0) {
+                    mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
+                }
+            } catch (RuntimeException e) {
+                Log.e(TAG, "putSmsMessagesToProvider", e);
+                DeferredSmsMmsRestoreService.localLog("putSmsMessagesToProvider: Exception " + e);
+                numExceptions++;
             }
         }
         if (msgCount % bulkInsertSize > 0) {
@@ -644,44 +701,103 @@
                     Arrays.copyOf(values, msgCount % bulkInsertSize));
         }
         jsonReader.endArray();
+        incremenentSharedPref(true, msgCount, numExceptions);
+    }
+
+    void incremenentSharedPref(boolean sms, int msgCount, int numExceptions) {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        SharedPreferences.Editor editor = sp.edit();
+        if (sms) {
+            editor.putInt(NUM_SMS_RESTORED, sp.getInt(NUM_SMS_RESTORED, 0) + msgCount);
+            editor.putInt(NUM_SMS_EXCEPTIONS, sp.getInt(NUM_SMS_EXCEPTIONS, 0) + numExceptions);
+            editor.putInt(NUM_SMS_FILES_STORED, sp.getInt(NUM_SMS_FILES_STORED, 0) + 1);
+            if (numExceptions > 0) {
+                editor.putInt(NUM_SMS_FILES_WITH_EXCEPTIONS,
+                        sp.getInt(NUM_SMS_FILES_WITH_EXCEPTIONS, 0) + 1);
+            }
+        } else {
+            editor.putInt(NUM_MMS_RESTORED, sp.getInt(NUM_MMS_RESTORED, 0) + msgCount);
+            editor.putInt(NUM_MMS_EXCEPTIONS, sp.getInt(NUM_MMS_EXCEPTIONS, 0) + numExceptions);
+            editor.putInt(NUM_MMS_FILES_STORED, sp.getInt(NUM_MMS_FILES_STORED, 0) + 1);
+            if (numExceptions > 0) {
+                editor.putInt(NUM_MMS_FILES_WITH_EXCEPTIONS,
+                        sp.getInt(NUM_MMS_FILES_WITH_EXCEPTIONS, 0) + 1);
+            }
+        }
+        editor.commit();
     }
 
     @VisibleForTesting
     void putMmsMessagesToProvider(JsonReader jsonReader) throws IOException {
         jsonReader.beginArray();
         int total = 0;
+        int numExceptions = 0;
         while (jsonReader.hasNext()) {
             final Mms mms = readMmsFromReader(jsonReader);
             if (DEBUG) {
                 Log.d(TAG, "putMmsMessagesToProvider " + mms);
             }
-            if (doesMmsExist(mms)) {
-                if (DEBUG) {
-                    Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
-                } else {
-                    Log.w(TAG, "Mms: Found duplicate MMS");
+            try {
+                if (doesMmsExist(mms)) {
+                    if (DEBUG) {
+                        Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
+                    } else {
+                        Log.w(TAG, "Mms: Found duplicate MMS");
+                    }
+                    continue;
                 }
-                continue;
+                total++;
+                addMmsMessage(mms);
+            } catch (Exception e) {
+                Log.e(TAG, "putMmsMessagesToProvider", e);
+                numExceptions++;
+                DeferredSmsMmsRestoreService.localLog("putMmsMessagesToProvider: Exception " + e);
             }
-            total++;
-            addMmsMessage(mms);
         }
         Log.d(TAG, "putMmsMessagesToProvider handled " + total + " new messages.");
+        incremenentSharedPref(false, total, numExceptions);
     }
 
     @VisibleForTesting
     static final String[] PROJECTION_ID = {BaseColumns._ID};
     private static final int ID_IDX = 0;
 
-    private boolean doesSmsExist(ContentValues smsValues) {
-        final String where = String.format(Locale.US, "%s = %d and %s = %s",
-                Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
-                Telephony.Sms.BODY,
-                DatabaseUtils.sqlEscapeString(smsValues.getAsString(Telephony.Sms.BODY)));
-        try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID, where,
-                null, null)) {
-            return cursor != null && cursor.getCount() > 0;
+    /**
+     * Interface to allow mocking method for testing.
+     */
+    public interface SmsProviderQuery {
+        boolean doesSmsExist(ContentValues smsValues);
+    }
+
+    private SmsProviderQuery mSmsProviderQuery = new SmsProviderQuery() {
+        @Override
+        public boolean doesSmsExist(ContentValues smsValues) {
+            // The SMS body might contain '\0' characters (U+0000) such as in the case of
+            // http://b/160801497 . SQLite does not allow '\0' in String literals, but as of SQLite
+            // version 3.32.2 2020-06-04, it does allow them as selectionArgs; therefore, we're
+            // using the latter approach here.
+            final String selection = String.format(Locale.US, "%s=%d AND %s=?",
+                    Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
+                    Telephony.Sms.BODY);
+            String[] selectionArgs = new String[] { smsValues.getAsString(Telephony.Sms.BODY)};
+            try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID,
+                    selection, selectionArgs, null)) {
+                return cursor != null && cursor.getCount() > 0;
+            }
         }
+    };
+
+    /**
+     * Sets a temporary {@code SmsProviderQuery} for testing; note that this method
+     * is not thread safe.
+     *
+     * @return the previous {@code SmsProviderQuery}
+     */
+    @VisibleForTesting
+    public SmsProviderQuery getAndSetSmsProviderQuery(SmsProviderQuery smsProviderQuery) {
+        SmsProviderQuery result = mSmsProviderQuery;
+        mSmsProviderQuery = smsProviderQuery;
+        return result;
     }
 
     private boolean doesMmsExist(Mms mms) {
@@ -1147,9 +1263,9 @@
         if (DEBUG) {
             Log.d(TAG, "Add mms:\n" + mms);
         }
-        final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+        final long placeholderId = System.currentTimeMillis(); // Placeholder ID of the msg.
         final Uri partUri = Telephony.Mms.CONTENT_URI.buildUpon()
-                .appendPath(String.valueOf(dummyId)).appendPath("part").build();
+                .appendPath(String.valueOf(placeholderId)).appendPath("part").build();
 
         final String srcName = String.format(Locale.US, "text.%06d.txt", 0);
         { // Insert SMIL part.
@@ -1157,7 +1273,7 @@
             final String smil = TextUtils.isEmpty(mms.smil) ?
                     String.format(sSmilTextOnly, smilBody) : mms.smil;
             final ContentValues values = new ContentValues(7);
-            values.put(Telephony.Mms.Part.MSG_ID, dummyId);
+            values.put(Telephony.Mms.Part.MSG_ID, placeholderId);
             values.put(Telephony.Mms.Part.SEQ, -1);
             values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.APP_SMIL);
             values.put(Telephony.Mms.Part.NAME, "smil.xml");
@@ -1172,7 +1288,7 @@
 
         { // Insert body part.
             final ContentValues values = new ContentValues(8);
-            values.put(Telephony.Mms.Part.MSG_ID, dummyId);
+            values.put(Telephony.Mms.Part.MSG_ID, placeholderId);
             values.put(Telephony.Mms.Part.SEQ, 0);
             values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.TEXT_PLAIN);
             values.put(Telephony.Mms.Part.NAME, srcName);
@@ -1194,7 +1310,7 @@
             // Insert the attachment parts.
             for (ContentValues mmsAttachment : mms.attachments) {
                 final ContentValues values = new ContentValues(6);
-                values.put(Telephony.Mms.Part.MSG_ID, dummyId);
+                values.put(Telephony.Mms.Part.MSG_ID, placeholderId);
                 values.put(Telephony.Mms.Part.SEQ, 0);
                 values.put(Telephony.Mms.Part.CONTENT_TYPE,
                         mmsAttachment.getAsString(MMS_MIME_TYPE));
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 4966e5a..0ff9fc6 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -71,6 +71,7 @@
 import static android.provider.Telephony.Carriers._ID;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -94,20 +95,23 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Telephony;
-import android.telephony.Annotation;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyProtoEnums;
 import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Xml;
@@ -115,6 +119,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.util.XmlUtils;
 import android.service.carrier.IApnSourceService;
 
@@ -125,9 +130,11 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.Integer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -137,6 +144,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.zip.CheckedInputStream;
 import java.util.zip.CRC32;
 
@@ -147,7 +155,7 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private static final int DATABASE_VERSION = 45 << 16;
+    private static final int DATABASE_VERSION = 51 << 16;
     private static final int URL_UNKNOWN = 0;
     private static final int URL_TELEPHONY = 1;
     private static final int URL_CURRENT = 2;
@@ -169,6 +177,9 @@
     private static final int URL_FILTERED = 18;
     private static final int URL_FILTERED_ID = 19;
     private static final int URL_ENFORCE_MANAGED = 20;
+    // URL_PREFERAPNSET and URL_PREFERAPNSET_USING_SUBID return all APNs for the current
+    // carrier which have an apn_set_id equal to the preferred APN
+    // (if no preferred APN, or preferred APN has no set id, the query will return null)
     private static final int URL_PREFERAPNSET = 21;
     private static final int URL_PREFERAPNSET_USING_SUBID = 22;
     private static final int URL_SIM_APN_LIST = 23;
@@ -176,6 +187,8 @@
     private static final int URL_FILTERED_USING_SUBID = 25;
     private static final int URL_SIM_APN_LIST_FILTERED = 26;
     private static final int URL_SIM_APN_LIST_FILTERED_ID = 27;
+    private static final int URL_SIMINFO_SUW_RESTORE = 28;
+    private static final int URL_SIMINFO_SIM_INSERTED_RESTORE = 29;
 
     /**
      * Default value for mtu if it's not set. Moved from PhoneConstants.
@@ -241,6 +254,19 @@
     private static final String ORDER_BY_SUB_ID =
             Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + " ASC";
 
+    @VisibleForTesting
+    static final String BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE = "sim_specific_settings_file";
+    // Holds names and value types of SimInfoDb columns to backup.
+    private static final Map<String, Integer> SIM_INFO_COLUMNS_TO_BACKUP = new HashMap();
+    private static final String KEY_SIMINFO_DB_ROW_PREFIX = "KEY_SIMINFO_DB_ROW_";
+    private static final int DEFAULT_INT_COLUMN_VALUE = -111;
+    private static final String DEFAULT_STRING_COLUMN_VALUE = "DEFAULT_STRING_COLUMN_VALUE";
+    private static final String SIM_INSERTED_RESTORE_URI_SUFFIX = "sim_inserted_restore";
+    @VisibleForTesting
+    static final String KEY_BACKUP_DATA_FORMAT_VERSION = "KEY_BACKUP_DATA_FORMAT_VERSION";
+    @VisibleForTesting
+    static final String KEY_PREVIOUSLY_RESTORED_SUB_IDS = "KEY_PREVIOUSLY_RESTORED_SUB_IDS";
+
     private static final int INVALID_APN_ID = -1;
     private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
     private static final Set<String> CARRIERS_BOOLEAN_FIELDS = new HashSet<String>();
@@ -257,6 +283,40 @@
     private boolean mManagedApnEnforced;
 
     /**
+     * Mobile country codes where there is a high likelyhood that the MNC has 3 digits
+     * and need one more prefix zero to set correct mobile network code value.
+     *
+     * Please note! The best solution is to add the MCCMNC combo to carrier id
+     * carrier_list, this is just a best effort.
+     */
+    private static final String[] COUNTRY_MCC_WITH_THREE_DIGIT_MNC = {
+            "302" // Canada
+           ,"310" // Guam, USA
+           ,"311" // USA
+           ,"312" // USA
+           ,"313" // USA
+           ,"316" // USA
+           ,"334" // Mexico
+           ,"338" // Bermuda, Jamaica
+           ,"342" // Barbados
+           ,"344" // Antigua and Barbuda
+           ,"346" // Cayman Islands
+           ,"348" // British Virgin Islands
+           ,"356" // Saint Kitts and Nevis
+           ,"358" // Saint Lucia
+           ,"360" // Saint Vincent and the Grenadines
+           ,"365" // Anguilla
+           ,"366" // Dominica
+           ,"376" // Turks and Caicos Islands
+           ,"405" // India
+           ,"708" // Honduras
+           ,"722" // Argentina
+           ,"732" // Colombia
+           ,"738" // Guyana
+           ,"750" // Falkland Islands
+            };
+
+    /**
      * Available radio technologies for GSM, UMTS and CDMA.
      * Duplicates the constants from hardware/radio/include/ril.h
      * This should only be used by agents working with the ril.  Others
@@ -349,6 +409,37 @@
         MVNO_TYPE_STRING_MAP.put("imsi", ApnSetting.MVNO_TYPE_IMSI);
         MVNO_TYPE_STRING_MAP.put("gid", ApnSetting.MVNO_TYPE_GID);
         MVNO_TYPE_STRING_MAP.put("iccid", ApnSetting.MVNO_TYPE_ICCID);
+
+        // To B&R a new config, simply add the column name and its appropriate value type to
+        // SIM_INFO_COLUMNS_TO_BACKUP. To no longer B&R a column, simply remove it from
+        // SIM_INFO_COLUMNS_TO_BACKUP. For both cases, add appropriate versioning logic in
+        // convertBackedUpDataToContentValues(ContentValues contenValues)
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_ICC_ID, Cursor.FIELD_TYPE_STRING);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_NUMBER, Cursor.FIELD_TYPE_STRING);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+            Telephony.SimInfo.COLUMN_CARRIER_ID, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+            Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_WFC_IMS_MODE, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                Cursor.FIELD_TYPE_STRING);
+
     }
 
     @VisibleForTesting
@@ -467,7 +558,13 @@
                 + Telephony.SimInfo.COLUMN_IMSI + " TEXT,"
                 + Telephony.SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED + " INTEGER DEFAULT 1,"
                 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1,"
-                + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0"
+                + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_RCS_CONFIG + " BLOB,"
+                + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS + " TEXT,"
+                + Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS + " TEXT"
                 + ");";
     }
 
@@ -482,6 +579,11 @@
 
         s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO);
         s_urlMatcher.addURI("telephony", "siminfo/#", URL_SIMINFO_USING_SUBID);
+        s_urlMatcher.addURI("telephony", "siminfo/backup_and_restore/suw_restore",
+                URL_SIMINFO_SUW_RESTORE);
+        s_urlMatcher.addURI("telephony", "siminfo/backup_and_restore/" +
+                SIM_INSERTED_RESTORE_URI_SUFFIX,
+                URL_SIMINFO_SIM_INSERTED_RESTORE);
 
         s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID);
         s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID);
@@ -749,7 +851,7 @@
                 try {
                     XmlUtils.beginDocument(parser, "apns");
                     publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
-                    loadApns(db, parser);
+                    loadApns(db, parser, true);
                 } catch (Exception e) {
                     loge("Got exception while loading APN database." + e);
                 } finally {
@@ -771,7 +873,7 @@
                 confparser.setInput(confreader);
                 XmlUtils.beginDocument(confparser, "apns");
 
-                // Sanity check. Force internal version and confidential versions to agree
+                // Correctness check. Force internal version and confidential versions to agree
                 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
                 if (publicversion != confversion) {
                     log("initDatabase: throwing exception due to version mismatch");
@@ -779,7 +881,7 @@
                             + confFile.getAbsolutePath());
                 }
 
-                loadApns(db, confparser);
+                loadApns(db, confparser, false);
             } catch (FileNotFoundException e) {
                 // It's ok if the file isn't found. It means there isn't a confidential file
                 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
@@ -1481,6 +1583,111 @@
                 oldVersion = 45 << 16 | 6;
             }
 
+            if (oldVersion < (46 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED
+                            + " INTEGER DEFAULT 0;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 46 << 16 | 6;
+            }
+
+            if (oldVersion < (47 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_RCS_CONFIG
+                            + " BLOB;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 47 << 16 | 6;
+            }
+
+            if (oldVersion < (48 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS
+                            + " TEXT;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                try {
+                    // Migrate the old Long values over to String
+                    String[] proj = {Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+                            Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES};
+                    try (Cursor c = db.query(SIMINFO_TABLE, proj, null, null, null, null, null)) {
+                        while (c.moveToNext()) {
+                            fillInAllowedNetworkTypesStringAtCursor(db, c);
+                        }
+                    }
+
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("can't migrate value from COLUMN_ALLOWED_NETWORK_TYPES to "
+                                + "COLUMN_ALLOWED_NETWORK_TYPES_ALL_REASON");
+                    }
+                }
+                oldVersion = 48 << 16 | 6;
+            }
+
+            if (oldVersion < (49 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS
+                            + " INTEGER DEFAULT 0;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. "
+                                + "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 49 << 16 | 6;
+            }
+
+            if (oldVersion < (50 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING
+                            + " INTEGER DEFAULT 0;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade failed to updated " + SIMINFO_TABLE
+                                + " to add d2d status sharing column. ");
+                    }
+                }
+                oldVersion = 50 << 16 | 6;
+            }
+
+            if (oldVersion < (51 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALERT TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS
+                            + " TEXT;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade failed to updated " + SIMINFO_TABLE
+                                + " to add d2d status sharing contacts. ");
+                    }
+                }
+                oldVersion = 51 << 16 | 6;
+            }
             if (DBG) {
                 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
             }
@@ -1666,7 +1873,7 @@
                 try {
                     XmlUtils.nextElement(parser);
                     while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        ContentValues row = getRow(parser);
+                        ContentValues row = getRow(parser, false);
                         if (row == null) {
                             throw new XmlPullParserException("Expected 'apn' tag", parser, null);
                         }
@@ -2010,7 +2217,7 @@
             int columnIndex = c.getColumnIndex(key);
             if (columnIndex != -1) {
                 String fromCursor = c.getString(columnIndex);
-                if (!TextUtils.isEmpty(fromCursor)) {
+                if (fromCursor != null) {
                     cv.put(key, fromCursor);
                 }
             }
@@ -2073,9 +2280,10 @@
          * Gets the next row of apn values.
          *
          * @param parser the parser
+         * @param isOverlay If the xml file comes from an overlay MCC/MNC are treated as integers
          * @return the row or null if it's not an apn
          */
-        private ContentValues getRow(XmlPullParser parser) {
+        private ContentValues getRow(XmlPullParser parser, boolean isOverlay) {
             if (!"apn".equals(parser.getName())) {
                 return null;
             }
@@ -2084,11 +2292,21 @@
 
             String mcc = parser.getAttributeValue(null, "mcc");
             String mnc = parser.getAttributeValue(null, "mnc");
-            String numeric = mcc + mnc;
+            String mccString = mcc;
+            String mncString = mnc;
+            // Since an mnc can have both two and three digits and it is hard to verify
+            // all OEM's Global APN lists we only do this for overlays.
+            if (isOverlay) {
+                mccString = String.format("%03d", Integer.parseInt(mcc));
+                // Looks up a two digit mnc in the carrier id DB
+                // if not found a three digit mnc value is chosen
+                mncString = getBestStringMnc(mContext, mccString, Integer.parseInt(mnc));
+            }
 
+            String numeric = mccString + mncString;
             map.put(NUMERIC, numeric);
-            map.put(MCC, mcc);
-            map.put(MNC, mnc);
+            map.put(MCC, mccString);
+            map.put(MNC, mncString);
             map.put(NAME, parser.getAttributeValue(null, "carrier"));
 
             // do not add NULL to the map so that default values can be inserted in db
@@ -2157,7 +2375,6 @@
                     map.put(MVNO_MATCH_DATA, mvno_match_data);
                 }
             }
-
             return map;
         }
 
@@ -2190,15 +2407,15 @@
          *
          * @param db the sqlite database to write to
          * @param parser the xml parser
-         *
+         * @param isOverlay, if we are parsing an xml in an overlay
          */
-        private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
+        private void loadApns(SQLiteDatabase db, XmlPullParser parser, boolean isOverlay) {
             if (parser != null) {
                 try {
                     db.beginTransaction();
                     XmlUtils.nextElement(parser);
                     while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        ContentValues row = getRow(parser);
+                        ContentValues row = getRow(parser, isOverlay);
                         if (row == null) {
                             throw new XmlPullParserException("Expected 'apn' tag", parser, null);
                         }
@@ -2621,8 +2838,10 @@
                 r.getString(R.string.apn_source_service)));
         log("binding to service to restore apns, intent=" + intent);
         try {
-            if (context.bindService(intent, connection, Context.BIND_IMPORTANT |
-                        Context.BIND_AUTO_CREATE)) {
+            if (context.bindService(intent,
+                    Context.BIND_IMPORTANT | Context.BIND_AUTO_CREATE,
+                    runnable -> new Thread(runnable).start(),
+                    connection)) {
                 synchronized (mLock) {
                     while (mIApnSourceService == null && !connectionBindingInvalid.get()) {
                         try {
@@ -2675,10 +2894,10 @@
 
         boolean isNewBuild = false;
         String newBuildId = SystemProperties.get("ro.build.id", null);
+        SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE,
+                Context.MODE_PRIVATE);
         if (!TextUtils.isEmpty(newBuildId)) {
             // Check if build id has changed
-            SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE,
-                    Context.MODE_PRIVATE);
             String oldBuildId = sp.getString(RO_BUILD_ID, "");
             if (!newBuildId.equals(oldBuildId)) {
                 localLog("onCreate: build id changed from " + oldBuildId + " to " + newBuildId);
@@ -2686,7 +2905,6 @@
             } else {
                 if (VDBG) log("onCreate: build id did not change: " + oldBuildId);
             }
-            sp.edit().putString(RO_BUILD_ID, newBuildId).apply();
         } else {
             if (VDBG) log("onCreate: newBuildId is empty");
         }
@@ -2701,9 +2919,15 @@
             if (DBG) addAllApnSharedPrefToLocalLog();
         }
 
-        SharedPreferences sp = getContext().getSharedPreferences(ENFORCED_FILE,
+        // Write build id to SharedPreferences after APNs have been updated above by updateApnDb()
+        if (!TextUtils.isEmpty(newBuildId)) {
+            if (isNewBuild) log("onCreate: updating build id to " + newBuildId);
+            sp.edit().putString(RO_BUILD_ID, newBuildId).apply();
+        }
+
+        SharedPreferences spEnforcedFile = getContext().getSharedPreferences(ENFORCED_FILE,
                 Context.MODE_PRIVATE);
-        mManagedApnEnforced = sp.getBoolean(ENFORCED_KEY, false);
+        mManagedApnEnforced = spEnforcedFile.getBoolean(ENFORCED_KEY, false);
 
         if (VDBG) log("onCreate:- ret true");
 
@@ -2911,6 +3135,533 @@
     }
 
     @Override
+    public synchronized Bundle call(String method, @Nullable String args, @Nullable Bundle bundle) {
+        if (SubscriptionManager.GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME.equals(method)) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, TAG);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return retrieveSimSpecificSettings();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else if (SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME.equals(method)) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_PHONE_STATE, TAG);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                restoreSimSpecificSettings(bundle, args);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        } else {
+            loge("method is not recognized");
+        }
+
+        return null;
+    }
+
+    /**
+     * See {@link SubscriptionController#GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for details
+     */
+    private Bundle retrieveSimSpecificSettings() {
+        Bundle resultBundle = new Bundle();
+        resultBundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA,
+                getSimSpecificDataToBackUp());
+
+        return resultBundle;
+    }
+
+    /**
+     * Attempts to restore the backed up sim-specific configs to device. End result is SimInfoDB is
+     * modified to match any backed up configs for the appropriate inserted sims.
+     *
+     * @param bundle containing the data to be restored. If {@code null}, then backed up
+     * data should already be in internal storage and will be retrieved from there.
+     * @param iccId of the SIM that a restore is being attempted for. If {@code null}, then try to
+     * restore for all simInfo entries in SimInfoDB
+     */
+    private void restoreSimSpecificSettings(@Nullable Bundle bundle, @Nullable String iccId) {
+        int restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_UNDEFINED_USE_CASE;
+        if (bundle != null) {
+            restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_SUW;
+            if (!writeSimSettingsToInternalStorage(
+                    bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA))) {
+                return;
+            }
+        } else if (iccId != null){
+            restoreCase = TelephonyProtoEnums.SIM_RESTORE_CASE_SIM_INSERTED;
+        }
+        mergeBackedUpDataToSimInfoDb(restoreCase, iccId);
+    }
+
+    @VisibleForTesting
+    boolean writeSimSettingsToInternalStorage(byte[] data) {
+        AtomicFile atomicFile = new AtomicFile(
+                new File(getContext().getFilesDir(), BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE));
+        FileOutputStream fos = null;
+        try {
+            fos = atomicFile.startWrite();
+            fos.write(data);
+            atomicFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                atomicFile.failWrite(fos);
+            }
+            loge("Not able to create internal file with per-sim configs. Failed with error "
+                    + e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempt to match any SimInfoDB entries to what is in the internal backup data file and
+     * update DB entry with the adequate backed up data.
+     *
+     * @param restoreCase one of the SimSpecificSettingsRestoreMatchingCriteria values defined in
+     * frameworks/proto_logging/stats/enums/telephony/enums.proto
+     * @param iccId of the SIM that a restore is being attempted for. If {@code null}, then try to
+     * restore for all simInfo entries in SimInfoDB
+     */
+    private void mergeBackedUpDataToSimInfoDb(int restoreCase, @Nullable String iccId) {
+        // Get data stored in internal file
+        File file = new File(getContext().getFilesDir(), BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE);
+        if (!file.exists()) {
+            loge("internal sim-specific settings backup data file does not exist. "
+                + "Aborting restore");
+            return;
+        }
+
+        AtomicFile atomicFile = new AtomicFile(file);
+        PersistableBundle bundle = null;
+        try (FileInputStream fis = atomicFile.openRead()) {
+            bundle = PersistableBundle.readFromStream(fis);
+        } catch (IOException e) {
+            loge("Failed to convert backed up per-sim configs to bundle. Stopping restore. "
+                + "Failed with error " + e);
+            return;
+        }
+
+        String selection = null;
+        String[] selectionArgs = null;
+        if (iccId != null) {
+            selection = Telephony.SimInfo.COLUMN_ICC_ID + "=?";
+            selectionArgs = new String[]{iccId};
+        }
+        try (Cursor cursor = query(
+                SubscriptionManager.CONTENT_URI,
+                new String[]{
+                        Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+                        Telephony.SimInfo.COLUMN_ICC_ID,
+                        Telephony.SimInfo.COLUMN_NUMBER,
+                        Telephony.SimInfo.COLUMN_CARRIER_ID,
+                        Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE},
+                selection,
+                selectionArgs,
+                ORDER_BY_SUB_ID)) {
+            findAndRestoreAllMatches(bundle.deepCopy(), cursor, restoreCase);
+        }
+    }
+
+    // backedUpDataBundle must to be mutable
+    private void findAndRestoreAllMatches(PersistableBundle backedUpDataBundle, Cursor cursor,
+            int restoreCase) {
+        int[] previouslyRestoredSubIdsArray =
+                backedUpDataBundle.getIntArray(KEY_PREVIOUSLY_RESTORED_SUB_IDS);
+        List<Integer> previouslyRestoredSubIdsList = previouslyRestoredSubIdsArray != null
+                ? Arrays.stream(previouslyRestoredSubIdsArray).boxed().collect(Collectors.toList())
+                : new ArrayList<>();
+        List<Integer> newlyRestoredSubIds = new ArrayList<>();
+        int backupDataFormatVersion = backedUpDataBundle
+                .getInt(KEY_BACKUP_DATA_FORMAT_VERSION, -1);
+
+        Resources r = getContext().getResources();
+        List<String> wfcRestoreBlockedCountries = Arrays.asList(r.getStringArray(
+                    R.array.wfc_restore_blocked_countries));
+
+        while (cursor != null && cursor.moveToNext()) {
+            // Get all the possible matching criteria.
+            int subIdColumnIndex = cursor.getColumnIndex(
+                    Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID);
+            int currSubIdFromDb = cursor.getInt(subIdColumnIndex);
+
+            if (previouslyRestoredSubIdsList.contains(currSubIdFromDb)) {
+                // Abort restore for any sims that were previously restored.
+                continue;
+            }
+
+            int iccIdColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_ICC_ID);
+            String currIccIdFromDb = cursor.getString(iccIdColumnIndex);
+
+            int phoneNumberColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_NUMBER);
+            String currPhoneNumberFromDb = cursor.getString(phoneNumberColumnIndex);
+
+            int carrierIdColumnIndex = cursor.getColumnIndex(Telephony.SimInfo.COLUMN_CARRIER_ID);
+            int currCarrierIdFromDb = cursor.getInt(carrierIdColumnIndex);
+
+            int isoCountryCodeColumnIndex= cursor.getColumnIndex(
+                    Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE);
+            String isoCountryCodeFromDb = cursor.getString(isoCountryCodeColumnIndex);
+
+
+            // Find the best match from backed up data.
+            SimRestoreMatch bestRestoreMatch = null;
+            for (int rowNum = 0; true; rowNum++) {
+                PersistableBundle currRow = backedUpDataBundle.getPersistableBundle(
+                        KEY_SIMINFO_DB_ROW_PREFIX + rowNum);
+                if (currRow == null) {
+                    break;
+                }
+
+                SimRestoreMatch currSimRestoreMatch = new SimRestoreMatch(
+                        currIccIdFromDb, currCarrierIdFromDb, currPhoneNumberFromDb,
+                        isoCountryCodeFromDb, wfcRestoreBlockedCountries, currRow,
+                        backupDataFormatVersion);
+
+                if (currSimRestoreMatch == null) {
+                    continue;
+                }
+
+                /*
+                 * The three following match cases are ordered by descending priority:
+                 *   - Match by iccId: If iccId of backup data matches iccId of any inserted sims,
+                 *       we confidently restore all configs.
+                 *   - Match phone number and carrierId: If both of these values match, we
+                 *       confidently restore all configs.
+                 *   - Match only carrierId: If only carrierId of backup data matches an inserted
+                 *       sim, we only restore non-sensitive configs.
+                 *
+                 * Having a matchScore value for each match allows us to control these priorities.
+                 */
+                if (bestRestoreMatch == null || (currSimRestoreMatch.getMatchScore()
+                        >= bestRestoreMatch.getMatchScore()
+                        && currSimRestoreMatch.getContentValues() != null)) {
+                    bestRestoreMatch = currSimRestoreMatch;
+                }
+            }
+
+            if (bestRestoreMatch != null) {
+                if (bestRestoreMatch.getMatchScore() != 0) {
+                    if (restoreCase == TelephonyProtoEnums.SIM_RESTORE_CASE_SUW) {
+                        update(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI,
+                                bestRestoreMatch.getContentValues(),
+                                Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+                                new String[]{Integer.toString(currSubIdFromDb)});
+                    } else if (restoreCase == TelephonyProtoEnums.SIM_RESTORE_CASE_SIM_INSERTED) {
+                        Uri simInsertedRestoreUri = Uri.withAppendedPath(
+                                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                                SIM_INSERTED_RESTORE_URI_SUFFIX);
+                        update(simInsertedRestoreUri,
+                                bestRestoreMatch.getContentValues(),
+                                Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+                                new String[]{Integer.toString(currSubIdFromDb)});
+                    }
+                    log("Restore of inserterd SIM's sim-specific settings has been successfully "
+                            + "completed.");
+                    TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED,
+                            TelephonyProtoEnums.SIM_RESTORE_RESULT_SUCCESS,
+                            restoreCase, bestRestoreMatch.getMatchingCriteriaForLogging());
+                    newlyRestoredSubIds.add(currSubIdFromDb);
+                } else {
+                    TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED,
+                            TelephonyProtoEnums.SIM_RESTORE_RESULT_NONE_MATCH,
+                            restoreCase, bestRestoreMatch.getMatchingCriteriaForLogging());
+                }
+            } else {
+                log("No matching SIM in backup data. SIM-specific settings not restored.");
+                TelephonyStatsLog.write(TelephonyStatsLog.SIM_SPECIFIC_SETTINGS_RESTORED,
+                        TelephonyProtoEnums.SIM_RESTORE_RESULT_ZERO_SIM_IN_BACKUP,
+                        restoreCase, TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_NONE);
+            }
+        }
+
+        // Update the internal file with subIds that we just restored.
+        previouslyRestoredSubIdsList.addAll(newlyRestoredSubIds);
+        backedUpDataBundle.putIntArray(
+                KEY_PREVIOUSLY_RESTORED_SUB_IDS,
+                previouslyRestoredSubIdsList.stream().mapToInt(i -> i).toArray());
+        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+            backedUpDataBundle.writeToStream(outputStream);
+            writeSimSettingsToInternalStorage(outputStream.toByteArray());
+        } catch (IOException e) {
+            loge("Not able to convert SimInfoDB to byte array. Not storing which subIds were "
+                    + "restored");
+        }
+    }
+
+    private static class SimRestoreMatch {
+
+        private Set<Integer> matches = new ArraySet<>();
+        private int subId;
+        private ContentValues contentValues;
+        private int matchingCriteria;
+        private int matchScore;
+
+        private static final int ICCID_MATCH = 1;
+        private static final int PHONE_NUMBER_MATCH = 2;
+        private static final int CARRIER_ID_MATCH = 3;
+
+        public SimRestoreMatch(String iccIdFromDb, int carrierIdFromDb,
+                String phoneNumberFromDb, String isoCountryCodeFromDb,
+                List<String> wfcRestoreBlockedCountries,
+                PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion) {
+            subId = backedUpSimInfoEntry.getInt(
+                Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
+                DEFAULT_INT_COLUMN_VALUE);
+            String iccIdFromBackup = backedUpSimInfoEntry.getString(Telephony.SimInfo.COLUMN_ICC_ID,
+                  "");
+            String phoneNumberFromBackup = backedUpSimInfoEntry.getString(
+                  Telephony.SimInfo.COLUMN_NUMBER, "");
+            int carrierIdFromBackup = backedUpSimInfoEntry.getInt(
+                  Telephony.SimInfo.COLUMN_CARRIER_ID,
+                  TelephonyManager.UNKNOWN_CARRIER_ID);
+
+
+            // find all matching fields
+            if (iccIdFromDb != null && iccIdFromDb.equals(iccIdFromBackup)
+                    && !iccIdFromBackup.isEmpty()) {
+                matches.add(ICCID_MATCH);
+            }
+            if (carrierIdFromDb == carrierIdFromBackup
+                    && carrierIdFromBackup != TelephonyManager.UNKNOWN_CARRIER_ID) {
+                matches.add(CARRIER_ID_MATCH);
+            }
+            if (phoneNumberFromDb != null && phoneNumberFromDb.equals(phoneNumberFromBackup)
+                    && !phoneNumberFromBackup.isEmpty()) {
+                matches.add(PHONE_NUMBER_MATCH);
+            }
+
+            contentValues = convertBackedUpDataToContentValues(
+                    backedUpSimInfoEntry, backupDataFormatVersion, isoCountryCodeFromDb,
+                    wfcRestoreBlockedCountries);
+            matchScore = calculateMatchScore();
+            matchingCriteria = calculateMatchingCriteria();
+        }
+
+        public int getSubId() {
+            return subId;
+        }
+
+        public ContentValues getContentValues() {
+            return contentValues;
+        }
+
+        public int getMatchScore() {
+            return matchScore;
+        }
+
+        private int calculateMatchScore() {
+            int score = 0;
+
+            if (matches.contains(ICCID_MATCH)) {
+                score += 100;
+            }
+            if (matches.contains(CARRIER_ID_MATCH)) {
+                if (matches.contains(PHONE_NUMBER_MATCH)) {
+                    score += 10;
+                } else {
+                    score += 1;
+                }
+            }
+
+            return score;
+        }
+
+        public int getMatchingCriteriaForLogging() {
+            return matchingCriteria;
+        }
+
+        private int calculateMatchingCriteria() {
+            if (matches.contains(ICCID_MATCH)) {
+                return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_ICCID;
+            }
+            if (matches.contains(CARRIER_ID_MATCH)) {
+                if (matches.contains(PHONE_NUMBER_MATCH)) {
+                    return TelephonyProtoEnums
+                        .SIM_RESTORE_MATCHING_CRITERIA_CARRIER_ID_AND_PHONE_NUMBER;
+                } else {
+                    return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_CARRIER_ID_ONLY;
+                }
+            }
+            return TelephonyProtoEnums.SIM_RESTORE_MATCHING_CRITERIA_NONE;
+        }
+
+        private ContentValues convertBackedUpDataToContentValues(
+                PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion,
+                String isoCountryCodeFromDb,
+                List<String> wfcRestoreBlockedCountries) {
+            if (DATABASE_VERSION != 51 << 16) {
+                throw new AssertionError("The database schema has been updated which might make "
+                    + "the format of #BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE outdated. Make sure to "
+                    + "1) review whether any of the columns in #SIM_INFO_COLUMNS_TO_BACKUP have "
+                    + "been migrated or deleted, 2) add the new column name into one of those "
+                    + "maps, 3) add migration code in this method as necessary, and 4) update the "
+                    + "version check in this if statement.");
+            }
+            ContentValues contentValues = new ContentValues();
+            // Don't restore anything if restoring from a newer version of the current database.
+            if (backupDataFormatVersion > DATABASE_VERSION) {
+                return null;
+            }
+
+            /* Any migration logic should be placed under this comment block.
+             * ex:
+             *   if (backupDataFormatVersion >= 48 << 19) {
+             *     contentValues.put(NEW_COLUMN_NAME_2,
+             *         backedUpSimInfoEntry.getInt( OLD_COLUMN_NAME, DEFAULT_INT_COLUMN_VALUE));
+             *     ...
+             *   } else if (backupDataFormatVersion >= 48 << 17) {
+             *     contentValues.put(NEW_COLUMN_NAME_1,
+             *         backedUpSimInfoEntry.getInt(OLD_COLUMN_NAME, DEFAULT_INT_COLUMN_VALUE));
+             *     ...
+             *   } else {
+             *     // The values from the first format of backup ever available.
+             *     contentValues.put(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+             *         backedUpSimInfoEntry.getInt(
+             *             Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+             *             DEFAULT_INT_COLUMN_VALUE));
+             *     contentValues.put(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+             *         backedUpSimInfoEntry.getString(
+             *              Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, ""));
+             *     contentValues.put(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED,
+             *               backedUpSimInfoEntry.getString(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED,
+             *               ""));
+             *     ...
+             *   }
+             *
+             * Also make sure to add necessary removal of sensitive settings in
+             * polishContentValues(ContentValues contentValues).
+             */
+            if (backupDataFormatVersion >= 51 << 16) {
+                contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                        backedUpSimInfoEntry.getString(
+                                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                                DEFAULT_STRING_COLUMN_VALUE));
+            }
+            if (backupDataFormatVersion >= 50 << 16) {
+                contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING,
+                        backedUpSimInfoEntry.getInt(
+                                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING,
+                                DEFAULT_INT_COLUMN_VALUE));
+            }
+            contentValues.put(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+                    backedUpSimInfoEntry.getInt(
+                            Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED,
+                            DEFAULT_INT_COLUMN_VALUE));
+            contentValues.put(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+                    backedUpSimInfoEntry.getInt(
+                            Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED,
+                            DEFAULT_INT_COLUMN_VALUE));
+            contentValues.put(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED,
+                    backedUpSimInfoEntry.getInt(
+                            Telephony.SimInfo.COLUMN_VT_IMS_ENABLED,
+                            DEFAULT_INT_COLUMN_VALUE));
+            if (isoCountryCodeFromDb != null
+                    && !wfcRestoreBlockedCountries
+                            .contains(isoCountryCodeFromDb.toLowerCase())) {
+                // Don't restore COLUMN_WFC_IMS_ENABLED if the sim is from one of the countries that
+                // requires WFC entitlement.
+                contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED,
+                        backedUpSimInfoEntry.getInt(
+                                Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED,
+                                DEFAULT_INT_COLUMN_VALUE));
+            }
+            contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_MODE,
+                    backedUpSimInfoEntry.getInt(
+                            Telephony.SimInfo.COLUMN_WFC_IMS_MODE,
+                            DEFAULT_INT_COLUMN_VALUE));
+            contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+                    backedUpSimInfoEntry.getInt(
+                            Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE,
+                            DEFAULT_INT_COLUMN_VALUE));
+
+            return polishContentValues(contentValues);
+        }
+
+        private ContentValues polishContentValues(ContentValues contentValues) {
+            /* Remove any values that weren't found in the backup file. These were set to defaults
+            in #convertBackedUpDataToContentValues(). */
+            for (Map.Entry<String, Integer> column : SIM_INFO_COLUMNS_TO_BACKUP.entrySet()) {
+                String columnName = column.getKey();
+
+                if (!contentValues.containsKey(columnName)) {
+                    continue;
+                }
+
+                int columnType = column.getValue();
+                if (columnType == Cursor.FIELD_TYPE_INTEGER
+                        && DEFAULT_INT_COLUMN_VALUE == contentValues.getAsInteger(columnName)) {
+                    contentValues.remove(columnName);
+                } else if (columnType == Cursor.FIELD_TYPE_STRING && contentValues
+                        .getAsString(columnName).equals(DEFAULT_STRING_COLUMN_VALUE)) {
+                    contentValues.remove(columnName);
+                }
+            }
+
+            if (matches.contains(ICCID_MATCH)) {
+                return contentValues;
+            } else if (matches.contains(CARRIER_ID_MATCH)) {
+                if (!matches.contains(PHONE_NUMBER_MATCH)) {
+                    // Low confidence match should not restore sensitive configs.
+                    if (contentValues.containsKey(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED)) {
+                        contentValues.remove(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
+                    }
+                }
+                return contentValues;
+            }
+            return null;
+        }
+
+    }
+
+    /**
+     * Retrieves data from all columns in SimInfoDB of backup/restore interest.
+     *
+     * @return data of interest from SimInfoDB as a byte array.
+     */
+    private byte[] getSimSpecificDataToBackUp() {
+        String[] projection = SIM_INFO_COLUMNS_TO_BACKUP.keySet()
+                .toArray(new String[SIM_INFO_COLUMNS_TO_BACKUP.size()]);
+
+        try (Cursor cursor = query(SubscriptionManager.CONTENT_URI, projection, null, null, null);
+                ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+            PersistableBundle topLevelBundle = new PersistableBundle();
+            topLevelBundle.putInt(KEY_BACKUP_DATA_FORMAT_VERSION, DATABASE_VERSION);
+            for (int rowNum = 0; cursor != null && cursor.moveToNext(); rowNum++) {
+                PersistableBundle rowBundle = convertSimInfoDbEntryToPersistableBundle(cursor);
+                topLevelBundle.putPersistableBundle(KEY_SIMINFO_DB_ROW_PREFIX + rowNum, rowBundle);
+            }
+            topLevelBundle.writeToStream(outputStream);
+            return outputStream.toByteArray();
+        } catch (IOException e) {
+            loge("Not able to convert SimInfoDB to byte array. Returning empty byte array");
+            return new byte[0];
+        }
+    }
+
+    private static PersistableBundle convertSimInfoDbEntryToPersistableBundle(Cursor cursor) {
+        PersistableBundle bundle = new PersistableBundle();
+        for (Map.Entry<String, Integer> column : SIM_INFO_COLUMNS_TO_BACKUP.entrySet()) {
+            String columnName = column.getKey();
+            int columnType = column.getValue();
+            int columnIndex = cursor.getColumnIndex(columnName);
+            if (columnType == Cursor.FIELD_TYPE_INTEGER) {
+                bundle.putInt(columnName, cursor.getInt(columnIndex));
+            } else if (columnType == Cursor.FIELD_TYPE_STRING) {
+                bundle.putString(columnName, cursor.getString(columnIndex));
+            } else {
+                throw new AssertionError("SimInfoDB column to be backed up is not recognized. Make "
+                    + "sure to properly add the desired colum name and value type to "
+                    + "SIM_INFO_COLUMNS_TO_BACKUP.");
+            }
+        }
+
+        return bundle;
+    }
+
+    @Override
     public synchronized Cursor query(Uri url, String[] projectionIn, String selection,
             String[] selectionArgs, String sort) {
         if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection="
@@ -2927,6 +3678,7 @@
         checkPermissionCompat(match, projectionIn);
         switch (match) {
             case URL_TELEPHONY_USING_SUBID: {
+                // The behaves exactly same as URL_SIM_APN_LIST_ID.
                 subIdString = url.getLastPathSegment();
                 try {
                     subId = Integer.parseInt(subIdString);
@@ -2935,13 +3687,13 @@
                     return null;
                 }
                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
-                TelephonyManager telephonyManager = getContext()
-                    .getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
-                constraints.add(NUMERIC + " = '" + telephonyManager.getSimOperator() + "'");
+                qb.appendWhereStandalone(IS_NOT_OWNED_BY_DPC);
+                return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs,
+                        sort, subId);
+
                 // TODO b/74213956 turn this back on once insertion includes correct sub id
                 // constraints.add(SUBSCRIPTION_ID + "=" + subIdString);
             }
-            // intentional fall through from above case
             case URL_TELEPHONY: {
                 constraints.add(IS_NOT_OWNED_BY_DPC);
                 break;
@@ -3009,10 +3761,13 @@
             // intentional fall through from above case
             case URL_PREFERAPNSET: {
                 final int set = getPreferredApnSetId(subId);
-                if (set != NO_APN_SET_ID) {
-                    constraints.add(APN_SET_ID + "=" + set);
+                if (set == NO_APN_SET_ID) {
+                    return null;
                 }
-                break;
+                constraints.add(APN_SET_ID + "=" + set);
+                qb.appendWhere(TextUtils.join(" AND ", constraints));
+                return getSubscriptionMatchingAPNList(qb, projectionIn, selection, selectionArgs,
+                        sort, subId);
             }
 
             case URL_DPC: {
@@ -3136,6 +3891,31 @@
     }
 
     /**
+     * This method syncs PREF_FILE_FULL_APN with the db based on the current preferred apn ids.
+     */
+    private void updatePreferredApns() {
+        SharedPreferences spApn = getContext().getSharedPreferences(PREF_FILE_APN,
+                Context.MODE_PRIVATE);
+
+        Map<String, ?> allPrefApnId = spApn.getAll();
+        for (String key : allPrefApnId.keySet()) {
+            if (key.startsWith(COLUMN_APN_ID)) {
+                int subId;
+                try {
+                    subId = Integer.parseInt(key.substring(COLUMN_APN_ID.length()));
+                } catch (NumberFormatException e) {
+                    loge("updatePreferredApns: NumberFormatException for key=" + key);
+                    continue;
+                }
+                long preferredApnId = getPreferredApnId(subId, false);
+                if (preferredApnId != INVALID_APN_ID) {
+                    setPreferredApn(preferredApnId, subId);
+                }
+            }
+        }
+    }
+
+    /**
      * To find the current sim APN. Query APN based on {MCC, MNC, MVNO} and {Carrier_ID}.
      *
      * There has three steps:
@@ -3147,7 +3927,14 @@
     private Cursor getSubscriptionMatchingAPNList(SQLiteQueryBuilder qb, String[] projectionIn,
             String selection, String[] selectionArgs, String sort, int subId) {
         Cursor ret;
-        final TelephonyManager tm = ((TelephonyManager) getContext()
+        Context context = getContext();
+        SubscriptionManager subscriptionManager = (SubscriptionManager) context
+                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        if (!subscriptionManager.isActiveSubscriptionId(subId)) {
+            return null;
+        }
+
+        final TelephonyManager tm = ((TelephonyManager) context
                 .getSystemService(Context.TELEPHONY_SERVICE))
                 .createForSubscriptionId(subId);
         SQLiteDatabase db = getReadableDatabase();
@@ -3190,10 +3977,19 @@
                 data.add(ret.getString(ret.getColumnIndex(column)));
             }
 
+            boolean isCurrentSimOperator;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                isCurrentSimOperator = tm.matchesCurrentSimOperator(
+                        ret.getString(numericIndex),
+                        getMvnoTypeIntFromString(ret.getString(mvnoIndex)),
+                        ret.getString(mvnoDataIndex));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
             boolean isMVNOAPN = !TextUtils.isEmpty(ret.getString(numericIndex))
-                    && tm.matchesCurrentSimOperator(ret.getString(numericIndex),
-                            getMvnoTypeIntFromString(ret.getString(mvnoIndex)),
-                            ret.getString(mvnoDataIndex));
+                    && isCurrentSimOperator;
             boolean isMNOAPN = !TextUtils.isEmpty(ret.getString(numericIndex))
                     && ret.getString(numericIndex).equals(mccmnc)
                     && TextUtils.isEmpty(ret.getString(mvnoIndex));
@@ -3461,6 +4257,7 @@
                 if (initialValues != null) {
                     if(initialValues.containsKey(COLUMN_APN_ID)) {
                         setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId, true);
+                        notify = true;
                     }
                 }
                 break;
@@ -3852,14 +4649,42 @@
                 break;
             }
 
+            case URL_SIMINFO_SUW_RESTORE:
+                count = db.update(SIMINFO_TABLE, values, where, whereArgs);
+                uriType = URL_SIMINFO_SUW_RESTORE;
+                break;
+
+            case URL_SIMINFO_SIM_INSERTED_RESTORE:
+                count = db.update(SIMINFO_TABLE, values, where, whereArgs);
+                break;
+
             default: {
                 throw new UnsupportedOperationException("Cannot update that URL: " + url);
             }
         }
 
+        // if APNs (CARRIERS_TABLE) have been updated, some of them may be preferred APN for
+        // different subs. So update the APN field values saved in SharedPref for all subIds.
+        switch (match) {
+            case URL_TELEPHONY_USING_SUBID:
+            case URL_TELEPHONY:
+            case URL_CURRENT_USING_SUBID:
+            case URL_CURRENT:
+            case URL_ID:
+            case URL_DPC_ID:
+                updatePreferredApns();
+                break;
+        }
+
         if (count > 0) {
             boolean usingSubId = false;
             switch (uriType) {
+                case URL_SIMINFO_SIM_INSERTED_RESTORE:
+                    break;
+                case URL_SIMINFO_SUW_RESTORE:
+                    getContext().getContentResolver().notifyChange(
+                            SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, null);
+                    // intentional fall through from above case
                 case URL_SIMINFO_USING_SUBID:
                     usingSubId = true;
                     // intentional fall through from above case
@@ -3910,6 +4735,18 @@
                                         Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED), usingSubId, subId),
                                 null, true, UserHandle.USER_ALL);
                     }
+                    if (values.containsKey(Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)) {
+                        getContext().getContentResolver().notifyChange(getNotifyContentUri(
+                                Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+                                        Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED),
+                                usingSubId, subId), null, true, UserHandle.USER_ALL);
+                    }
+                    if (values.containsKey(Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS)) {
+                        getContext().getContentResolver().notifyChange(getNotifyContentUri(
+                                Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+                                        Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS),
+                                usingSubId, subId), null, true, UserHandle.USER_ALL);
+                    }
                     break;
                 default:
                     getContext().getContentResolver().notifyChange(
@@ -3936,14 +4773,18 @@
 
         TelephonyManager telephonyManager =
                 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        for (String pkg : packages) {
-            if (telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(pkg) ==
+        final long token = Binder.clearCallingIdentity();
+        try {
+            for (String pkg : packages) {
+                if (telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(pkg) ==
                     TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-                return;
+                    return;
+                }
             }
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
-
         throw new SecurityException("No permission to access APN settings");
     }
 
@@ -4131,18 +4972,32 @@
         }
         String twoDigitMnc = String.format(Locale.getDefault(), "%02d", mnc);
         String threeDigitMnc = "0" + twoDigitMnc;
+        boolean threeDigitNetworkCode =
+                Arrays.asList(COUNTRY_MCC_WITH_THREE_DIGIT_MNC).contains(mcc);
+        int twoDigitResult = countMccMncInCarrierList(context, mcc + twoDigitMnc);
+        int threeDigitResult = countMccMncInCarrierList(context, mcc + threeDigitMnc);
 
-        try (
-                Cursor twoDigitMncCursor = context.getContentResolver().query(
-                        Telephony.CarrierId.All.CONTENT_URI,
-                        /* projection */ null,
-                        /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
-                        /* selectionArgs */ new String[]{mcc + twoDigitMnc}, null)
-        ) {
-            if (twoDigitMncCursor.getCount() > 0) {
-                return twoDigitMnc;
-            }
+        if ((threeDigitResult > twoDigitResult) ||
+                (threeDigitNetworkCode && (twoDigitResult == threeDigitResult))) {
             return threeDigitMnc;
+        } else {
+            return twoDigitMnc;
+        }
+    }
+
+    /**
+     * Check carrier_list how many mcc mnc combo matches there are
+     */
+    private static int countMccMncInCarrierList(Context ctx, String mccMncCombo) {
+        try (
+            Cursor mccMncCursor = ctx.getContentResolver().query(
+                    Telephony.CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
+                    /* selectionArgs */ new String[]{mccMncCombo}, null);
+        )
+        {
+            return mccMncCursor.getCount();
         }
     }
 
@@ -4320,4 +5175,36 @@
         }
         return 0;
     }
+
+    /**
+     * Migrate the old Long values{@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES} over to
+     * String{@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_ALL_REASON}
+     *
+     * @param db The sqlite database to write to
+     * @param c The {@link Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES} values in the sim info
+     *         table.
+     */
+    public static void fillInAllowedNetworkTypesStringAtCursor(SQLiteDatabase db, Cursor c) {
+        long allowedNetworkTypesReasonCarrier;
+        String subId;
+        try {
+            allowedNetworkTypesReasonCarrier = c.getLong(
+                    c.getColumnIndexOrThrow(Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES));
+            subId = c.getString(c.getColumnIndexOrThrow(
+                    Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Possible database corruption -- some columns not found.");
+            return;
+        }
+
+        if (allowedNetworkTypesReasonCarrier != -1) {
+            ContentValues cv = new ContentValues(1);
+
+            cv.put(Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS,
+                    "carrier=" + allowedNetworkTypesReasonCarrier);
+            db.update(SIMINFO_TABLE, cv,
+                    Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+                    new String[]{subId});
+        }
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index f1be84b..dafd7cd 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,3 +1,14 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_providers_TelephonyProvider_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: [
+        "packages_providers_TelephonyProvider_license",
+    ],
+}
+
 android_test {
     name: "TelephonyProviderTests",
     static_libs: [
diff --git a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
index ee0e016..921b59c 100644
--- a/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierIdProviderTest.java
@@ -58,18 +58,18 @@
 
     private static final String TAG = CarrierIdProviderTest.class.getSimpleName();
 
-    private static final String dummy_mccmnc = "MCCMNC_DUMMY";
-    private static final String dummy_gid1 = "GID1_DUMMY";
-    private static final String dummy_gid2 = "GID2_DUMMY";
-    private static final String dummy_plmn = "PLMN_DUMMY";
-    private static final String dummy_imsi_prefix = "IMSI_PREFIX_DUMMY";
-    private static final String dummy_spn = "SPN_DUMMY";
-    private static final String dummy_apn = "APN_DUMMY";
-    private static final String dummy_iccid_prefix = "ICCID_PREFIX_DUMMY";
-    private static final String dummy_name = "NAME_DUMMY";
-    private static final String dummy_access_rule =
+    private static final String test_mccmnc = "MCCMNC_TEST";
+    private static final String test_gid1 = "GID1_TEST";
+    private static final String test_gid2 = "GID2_TEST";
+    private static final String test_plmn = "PLMN_TEST";
+    private static final String test_imsi_prefix = "IMSI_PREFIX_TEST";
+    private static final String test_spn = "SPN_TEST";
+    private static final String test_apn = "APN_TEST";
+    private static final String test_iccid_prefix = "ICCID_PREFIX_TEST";
+    private static final String test_name = "NAME_TEST";
+    private static final String test_access_rule =
             "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350";
-    private static final int dummy_cid = 0;
+    private static final int test_cid = 0;
 
     private MockContextWithProvider mContext;
     private MockContentResolver mContentResolver;
@@ -204,7 +204,7 @@
         try {
             //insert a row with null mnccmnc to break not null constraint
             ContentValues contentValues = new ContentValues();
-            contentValues.put(CarrierId.All.GID1, dummy_gid1);
+            contentValues.put(CarrierId.All.GID1, test_gid1);
             mContentResolver.insert(CarrierId.All.CONTENT_URI, contentValues);
             Assert.fail("should throw an exception for null mccmnc");
         } catch (SQLException e) {
@@ -227,7 +227,7 @@
         int numRowsDeleted = -1;
         try {
             String whereClause = CarrierId.All.MCCMNC + "=?";
-            String[] whereArgs = new String[] { dummy_mccmnc };
+            String[] whereArgs = new String[] { test_mccmnc };
             numRowsDeleted = mContentResolver.delete(CarrierId.All.CONTENT_URI,
                     whereClause, whereArgs);
         } catch (Exception e) {
@@ -254,7 +254,7 @@
         try {
             contentValues.put(CarrierId.CARRIER_ID, 1);
             mContentResolver.update(CarrierId.All.CONTENT_URI, contentValues,
-                    CarrierId.All.MCCMNC + "=?", new String[] { dummy_mccmnc });
+                    CarrierId.All.MCCMNC + "=?", new String[] { test_mccmnc });
         } catch (Exception e) {
             Log.d(TAG, "Error updating values:" + e);
         }
@@ -262,7 +262,7 @@
         try {
             Cursor findEntry = mContentResolver.query(CarrierId.All.CONTENT_URI,
                     new String[] { CarrierId.CARRIER_ID},
-                    CarrierId.All.MCCMNC + "=?", new String[] { dummy_mccmnc },
+                    CarrierId.All.MCCMNC + "=?", new String[] { test_mccmnc },
                     null);
             findEntry.moveToFirst();
             cid = findEntry.getInt(0);
@@ -282,7 +282,7 @@
             mContentResolver.insert(CarrierId.All.CONTENT_URI, contentValues);
             // insert its MNO
             contentValues = new ContentValues();
-            contentValues.put(CarrierId.All.MCCMNC, dummy_mccmnc);
+            contentValues.put(CarrierId.All.MCCMNC, test_mccmnc);
             contentValues.put(CarrierId.CARRIER_ID, 1);
             mContentResolver.insert(CarrierId.All.CONTENT_URI, contentValues);
         } catch (Exception e) {
@@ -293,7 +293,7 @@
         String[] columns = {CarrierId.CARRIER_ID, CarrierId.All.ICCID_PREFIX};
         try {
             findEntry = mContentResolver.query(CarrierId.All.CONTENT_URI, columns,
-                    CarrierId.All.MCCMNC + "=?", new String[] { dummy_mccmnc },
+                    CarrierId.All.MCCMNC + "=?", new String[] { test_mccmnc },
                     null);
         } catch (Exception e) {
             Log.d(TAG, "Query failed:" + e);
@@ -306,14 +306,14 @@
                     CarrierId.All.MCCMNC + "=? and "
                     + CarrierId.All.GID1 + "=? and "
                     + CarrierId.All.ICCID_PREFIX + "=?",
-                    new String[] { dummy_mccmnc, dummy_gid1, dummy_iccid_prefix }, null);
+                    new String[] { test_mccmnc, test_gid1, test_iccid_prefix }, null);
         } catch (Exception e) {
             Log.d(TAG, "Query failed:" + e);
         }
         assertEquals(1, findEntry.getCount());
         findEntry.moveToFirst();
-        assertEquals(dummy_cid, findEntry.getInt(0));
-        assertEquals(dummy_iccid_prefix, findEntry.getString(1));
+        assertEquals(test_cid, findEntry.getInt(0));
+        assertEquals(test_iccid_prefix, findEntry.getString(1));
     }
 
     @Test
@@ -337,8 +337,8 @@
         // update carrier id for subId 1
         try {
             ContentValues cv = new ContentValues();
-            cv.put(CarrierId.CARRIER_ID, dummy_cid);
-            cv.put(CarrierId.CARRIER_NAME, dummy_name);
+            cv.put(CarrierId.CARRIER_ID, test_cid);
+            cv.put(CarrierId.CARRIER_NAME, test_name);
             when(subscriptionManager.isActiveSubscriptionId(eq(1))).thenReturn(true);
             mContext.getContentResolver().update(Uri.withAppendedPath(CarrierId.CONTENT_URI,
                     "1"), cv, null, null);
@@ -359,8 +359,8 @@
         } catch (Exception e) {
             Log.d(TAG, "Error query current subscription: " + e);
         }
-        assertEquals(dummy_cid, carrierId);
-        assertEquals(dummy_name, carrierName);
+        assertEquals(test_cid, carrierId);
+        assertEquals(test_name, carrierName);
 
         // query carrier id for subId 2
         int count  = -1;
@@ -384,8 +384,8 @@
         } catch (Exception e) {
             Log.d(TAG, "Error query current subscription: " + e);
         }
-        assertEquals(dummy_cid, carrierId);
-        assertEquals(dummy_name, carrierName);
+        assertEquals(test_cid, carrierId);
+        assertEquals(test_name, carrierName);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -410,8 +410,8 @@
     public void testUpdateCurrentSubscription_WrongURI() {
         try {
             ContentValues cv = new ContentValues();
-            cv.put(CarrierId.CARRIER_ID, dummy_cid);
-            cv.put(CarrierId.CARRIER_NAME, dummy_name);
+            cv.put(CarrierId.CARRIER_ID, test_cid);
+            cv.put(CarrierId.CARRIER_NAME, test_name);
             mContext.getContentResolver().update(CarrierId.CONTENT_URI, cv, null, null);
             Assert.fail("should throw an exception for wrong uri");
         } catch (IllegalArgumentException ex) {
@@ -421,17 +421,17 @@
 
     private static ContentValues createCarrierInfoInternal() {
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierId.All.MCCMNC, dummy_mccmnc);
-        contentValues.put(CarrierId.All.GID1, dummy_gid1);
-        contentValues.put(CarrierId.All.GID2, dummy_gid2);
-        contentValues.put(CarrierId.All.PLMN, dummy_plmn);
-        contentValues.put(CarrierId.All.IMSI_PREFIX_XPATTERN, dummy_imsi_prefix);
-        contentValues.put(CarrierId.All.SPN, dummy_spn);
-        contentValues.put(CarrierId.All.APN, dummy_apn);
-        contentValues.put(CarrierId.All.ICCID_PREFIX, dummy_iccid_prefix);
-        contentValues.put(CarrierId.CARRIER_NAME, dummy_name);
-        contentValues.put(CarrierId.CARRIER_ID, dummy_cid);
-        contentValues.put(CarrierId.All.PRIVILEGE_ACCESS_RULE, dummy_access_rule);
+        contentValues.put(CarrierId.All.MCCMNC, test_mccmnc);
+        contentValues.put(CarrierId.All.GID1, test_gid1);
+        contentValues.put(CarrierId.All.GID2, test_gid2);
+        contentValues.put(CarrierId.All.PLMN, test_plmn);
+        contentValues.put(CarrierId.All.IMSI_PREFIX_XPATTERN, test_imsi_prefix);
+        contentValues.put(CarrierId.All.SPN, test_spn);
+        contentValues.put(CarrierId.All.APN, test_apn);
+        contentValues.put(CarrierId.All.ICCID_PREFIX, test_iccid_prefix);
+        contentValues.put(CarrierId.CARRIER_NAME, test_name);
+        contentValues.put(CarrierId.CARRIER_ID, test_cid);
+        contentValues.put(CarrierId.All.PRIVILEGE_ACCESS_RULE, test_access_rule);
         return contentValues;
     }
 }
diff --git a/tests/src/com/android/providers/telephony/CarrierProviderTest.java b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
index 5146aa9..eb95fb6 100644
--- a/tests/src/com/android/providers/telephony/CarrierProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
@@ -54,16 +54,16 @@
     private MockContentResolver mContentResolver;
     private CarrierProviderTestable mCarrierProviderTestable;
 
-    public static final int dummy_type = 1;
-    public static final String dummy_mnc = "MNC001";
-    public static final String dummy_mnc2 = "MNC002";
-    public static final String dummy_mcc = "MCC005";
-    public static final String dummy_key1 = "PUBKEY1";
-    public static final String dummy_key2 = "PUBKEY2";
-    public static final String dummy_mvno_type = "100";
-    public static final String dummy_mvno_match_data = "101";
-    public static final String  dummy_key_identifier_data = "key_identifier1";
-    public static final long  dummy_key_expiration = 1496795015L;
+    public static final int test_type = 1;
+    public static final String test_mnc = "MNC001";
+    public static final String test_mnc2 = "MNC002";
+    public static final String test_mcc = "MCC005";
+    public static final String test_key1 = "PUBKEY1";
+    public static final String test_key2 = "PUBKEY2";
+    public static final String test_mvno_type = "100";
+    public static final String test_mvno_match_data = "101";
+    public static final String  test_key_identifier_data = "key_identifier1";
+    public static final long  test_key_expiration = 1496795015L;
 
 
     /**
@@ -144,14 +144,14 @@
     public void testInsertCertificates() {
         int count = -1;
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
-        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
-        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, test_key_expiration);
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -180,14 +180,14 @@
     public void testUpdateCertificates() {
         String key = null;
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
-        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
-        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, test_key_expiration);
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -197,10 +197,10 @@
 
         try {
             ContentValues updatedValues = new ContentValues();
-            updatedValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key2);
+            updatedValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key2);
             mContentResolver.update(CarrierProvider.CONTENT_URI, updatedValues,
-                    "mcc=? and mnc=? and key_type=?", new String[] { dummy_mcc, dummy_mnc,
-                            String.valueOf(dummy_type) });
+                    "mcc=? and mnc=? and key_type=?", new String[] { test_mcc, test_mnc,
+                            String.valueOf(test_type) });
         } catch (Exception e) {
             Log.d(TAG, "Error updating values:" + e);
         }
@@ -209,13 +209,13 @@
             String[] columns ={CarrierDatabaseHelper.PUBLIC_KEY};
             Cursor findEntry = mContentResolver.query(CarrierProvider.CONTENT_URI, columns,
                     "mcc=? and mnc=? and key_type=?",
-                    new String[] { dummy_mcc, dummy_mnc, String.valueOf(dummy_type) }, null);
+                    new String[] { test_mcc, test_mnc, String.valueOf(test_type) }, null);
             findEntry.moveToFirst();
             key = findEntry.getString(0);
         } catch (Exception e) {
             Log.d(TAG, "Query failed:" + e);
         }
-        assertEquals(key, dummy_key2);
+        assertEquals(key, test_key2);
     }
 
     /**
@@ -226,22 +226,22 @@
     public void testMultipleCertificates() {
         int count = -1;
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
-        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
 
         ContentValues contentValuesNew = new ContentValues();
-        contentValuesNew.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValuesNew.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValuesNew.put(CarrierDatabaseHelper.MNC, dummy_mnc2);
-        contentValuesNew.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValuesNew.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
-        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key2.getBytes());
+        contentValuesNew.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValuesNew.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValuesNew.put(CarrierDatabaseHelper.MNC, test_mnc2);
+        contentValuesNew.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValuesNew.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key2.getBytes());
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -270,12 +270,12 @@
     @Test(expected = SQLException.class)
     public void testDuplicateFailure() {
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
-        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -297,14 +297,14 @@
     public void testDelete() {
         int numRowsDeleted = -1;
         ContentValues contentValues = new ContentValues();
-        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
-        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
-        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
-        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
-        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
-        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, test_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, test_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, test_key_expiration);
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -314,7 +314,7 @@
 
         try {
             String whereClause = "mcc=? and mnc=?";
-            String[] whereArgs = new String[] { dummy_mcc, dummy_mnc };
+            String[] whereArgs = new String[] { test_mcc, test_mnc };
             numRowsDeleted = mContentResolver.delete(CarrierProvider.CONTENT_URI, whereClause, whereArgs);
         } catch (Exception e) {
             Log.d(TAG, "Error updating values:" + e);
diff --git a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
index b1cd5e4..f8cc8a6 100644
--- a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
@@ -41,7 +41,6 @@
 import android.util.ArraySet;
 import android.util.JsonReader;
 import android.util.JsonWriter;
-import android.util.Log;
 import android.util.SparseArray;
 
 import libcore.io.IoUtils;
@@ -616,6 +615,39 @@
     }
 
     /**
+     * Test that crashing for one sms does not block restore of other messages.
+     * @throws Exception
+     */
+    public void testRestoreSms_WithException() throws Exception {
+        mTelephonyBackupAgent.initUnknownSender();
+        JsonReader jsonReader = new JsonReader(new StringReader(addRandomDataToJson(mAllSmsJson)));
+        FakeSmsProvider smsProvider = new FakeSmsProvider(mSmsRows, false);
+        mMockContentResolver.addProvider("sms", smsProvider);
+        TelephonyBackupAgent.SmsProviderQuery smsProviderQuery =
+                new TelephonyBackupAgent.SmsProviderQuery() {
+                    int mIteration = 0;
+                    @Override
+                    public boolean doesSmsExist(ContentValues smsValues) {
+                        if (mIteration == 0) {
+                            mIteration++;
+                            throw new RuntimeException("fake crash for first message");
+                        }
+                        return false;
+                    }
+        };
+        TelephonyBackupAgent.SmsProviderQuery previousQuery =
+                mTelephonyBackupAgent.getAndSetSmsProviderQuery(smsProviderQuery);
+        try {
+            mTelephonyBackupAgent.putSmsMessagesToProvider(jsonReader);
+            // the "- 1" is due to exception thrown for one of the messages
+            assertEquals(mSmsRows.length - 1, smsProvider.getRowsAdded());
+            assertEquals(mThreadProvider.mIsThreadArchived, mThreadProvider.mUpdateThreadsArchived);
+        } finally {
+            mTelephonyBackupAgent.getAndSetSmsProviderQuery(previousQuery);
+        }
+    }
+
+    /**
      * Test restore mms with the empty json array "[]".
      * @throws Exception
      */
@@ -751,11 +783,17 @@
     private class FakeSmsProvider extends MockContentProvider {
         private int nextRow = 0;
         private ContentValues[] mSms;
+        private boolean mCheckInsertedValues = true;
 
         public FakeSmsProvider(ContentValues[] sms) {
             this.mSms = sms;
         }
 
+        public FakeSmsProvider(ContentValues[] sms, boolean checkInsertedValues) {
+            this.mSms = sms;
+            mCheckInsertedValues = checkInsertedValues;
+        }
+
         @Override
         public Uri insert(Uri uri, ContentValues values) {
             assertEquals(Telephony.Sms.CONTENT_URI, uri);
@@ -771,7 +809,7 @@
                 modifiedValues.put(Telephony.Sms.ADDRESS, TelephonyBackupAgent.UNKNOWN_SENDER);
             }
 
-            assertEquals(modifiedValues, values);
+            if (mCheckInsertedValues) assertEquals(modifiedValues, values);
             return null;
         }
 
@@ -800,7 +838,7 @@
     private class FakeMmsProvider extends MockContentProvider {
         private int nextRow = 0;
         private List<ContentValues> mValues;
-        private long mDummyMsgId = -1;
+        private long mPlaceholderMsgId = -1;
         private long mMsgId = -1;
         private String mFilename;
 
@@ -810,7 +848,7 @@
 
         @Override
         public Uri insert(Uri uri, ContentValues values) {
-            Uri retUri = Uri.parse("dummy_uri");
+            Uri retUri = Uri.parse("test_uri");
             ContentValues modifiedValues = new ContentValues(mValues.get(nextRow++));
             if (values.containsKey("read")) {
                 assertEquals("read: ", modifiedValues.get("read"), values.get("read"));
@@ -820,8 +858,8 @@
             }
             if (APP_SMIL.equals(values.get(Telephony.Mms.Part.CONTENT_TYPE))) {
                 // Smil part.
-                assertEquals(-1, mDummyMsgId);
-                mDummyMsgId = values.getAsLong(Telephony.Mms.Part.MSG_ID);
+                assertEquals(-1, mPlaceholderMsgId);
+                mPlaceholderMsgId = values.getAsLong(Telephony.Mms.Part.MSG_ID);
             }
             if (IMAGE_JPG.equals(values.get(Telephony.Mms.Part.CONTENT_TYPE))) {
                 // Image attachment part.
@@ -839,7 +877,7 @@
             if (values.get(Telephony.Mms.Part.SEQ) != null) {
                 // Part of mms.
                 final Uri expectedUri = Telephony.Mms.CONTENT_URI.buildUpon()
-                        .appendPath(String.valueOf(mDummyMsgId))
+                        .appendPath(String.valueOf(mPlaceholderMsgId))
                         .appendPath("part")
                         .build();
                 assertEquals(expectedUri, uri);
@@ -852,7 +890,7 @@
             }
 
             if (values.get(Telephony.Mms.Part.MSG_ID) != null) {
-                modifiedValues.put(Telephony.Mms.Part.MSG_ID, mDummyMsgId);
+                modifiedValues.put(Telephony.Mms.Part.MSG_ID, mPlaceholderMsgId);
             }
             if (values.containsKey("read")) {
                 assertEquals("read: ", modifiedValues.get("read"), values.get("read"));
@@ -890,7 +928,7 @@
                 assertEquals(expectedUri, uri);
                 assertNotSame(-1, mMsgId);
                 modifiedValues.put(Telephony.Mms.Addr.MSG_ID, mMsgId);
-                mDummyMsgId = -1;
+                mPlaceholderMsgId = -1;
             }
             if (values.containsKey("read")) {
                 assertEquals("read: ", modifiedValues.get("read"), values.get("read"));
@@ -909,7 +947,7 @@
         @Override
         public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
             final Uri expectedUri = Telephony.Mms.CONTENT_URI.buildUpon()
-                    .appendPath(String.valueOf(mDummyMsgId))
+                    .appendPath(String.valueOf(mPlaceholderMsgId))
                     .appendPath("part")
                     .build();
             assertEquals(expectedUri, uri);
diff --git a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
index 0666169..6f3f842 100644
--- a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -201,6 +201,71 @@
                 Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
     }
 
+    @Test
+    public void databaseHelperOnUpgrade_hasRcsConfigField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasRcsConfigField");
+        // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the Telephony.SimInfo.COLUMN_RCS_CONFIG field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+        assertTrue(Arrays.asList(upgradedColumns).contains(
+                Telephony.SimInfo.COLUMN_RCS_CONFIG));
+    }
+
+    @Test
+    public void databaseHelperOnUpgrade_hasVoImsOptInStatusField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasImsRcsUceEnabledField");
+        // (5 << 16) is the first upgrade trigger in onUpgrade
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, 4 << 16, TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the SubscriptionManager.VOIMS_OPT_IN_STATUS field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+        assertTrue(Arrays.asList(upgradedColumns).contains(
+                Telephony.SimInfo.COLUMN_VOIMS_OPT_IN_STATUS));
+    }
+
+    @Test
+    public void databaseHelperOnUpgrade_hasD2DStatusSharingField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasD2DStatusSharingField");
+        // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the Telephony.SimInfo.COLUMN_D2D_SHARING_STATUS field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+        assertTrue(Arrays.asList(upgradedColumns).contains(
+                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING));
+    }
+
+    @Test
+    public void databaseHelperOnUpgrade_hasD2DSharingContactsField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasD2DSharingContactsField");
+        // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the
+        // Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+        assertTrue(Arrays.asList(upgradedColumns).contains(
+                Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS));
+    }
+
     /**
      * Helper for an in memory DB used to test the TelephonyProvider#DatabaseHelper.
      *
diff --git a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
index 18c8d08..37ba7ec 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -34,6 +34,9 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Environment;
+import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.provider.Telephony;
 import android.provider.Telephony.Carriers;
@@ -42,6 +45,7 @@
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
+import android.test.mock.MockResources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.Log;
@@ -51,11 +55,13 @@
 import junit.framework.TestCase;
 
 import org.junit.Test;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Mock;
+import static org.mockito.Mockito.when;
 
-import java.lang.reflect.Field;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.IntStream;
@@ -78,6 +84,8 @@
     private MockContextWithProvider mContext;
     private MockContentResolver mContentResolver;
     private TelephonyProviderTestable mTelephonyProviderTestable;
+    @Mock
+    private Resources mockContextResources;
 
     private int notifyChangeCount;
     private int notifyChangeRestoreCount;
@@ -115,6 +123,101 @@
     private static final Uri URI_ENFORCE_MANAGED= Uri.parse("content://telephony/carriers/enforce_managed");
     private static final String ENFORCED_KEY = "enforced";
 
+
+    private static final String MATCHING_ICCID = "MATCHING_ICCID";
+    private static final String MATCHING_PHONE_NUMBER = "MATCHING_PHONE_NUMBER";
+    private static final int MATCHING_CARRIER_ID = 123456789;
+
+    // Represents an entry in the SimInfoDb
+    private static final ContentValues TEST_SIM_INFO_VALUES_US;
+    private static final ContentValues TEST_SIM_INFO_VALUES_FR;
+    private static final int ARBITRARY_SIMINFO_DB_TEST_INT_VALUE = 999999;
+    private static final String ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE
+            = "ARBITRARY_TEST_STRING_VALUE";
+
+    private static final ContentValues BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_ICCID;
+    private static final int ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1 = 111111;
+    private static final String ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_1
+            = "ARBITRARY_TEST_STRING_VALUE_1";
+
+    private static final ContentValues BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_NUMBER_AND_CID;
+    private static final int ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2 = 222222;
+    private static final String ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_2
+            = "ARBITRARY_TEST_STRING_VALUE_2";
+
+    private static final ContentValues BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID;
+    private static final int ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3 = 333333;
+    private static final String ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_3
+            = "ARBITRARY_TEST_STRING_VALUE_3";
+
+    static {
+        TEST_SIM_INFO_VALUES_US = populateContentValues(
+                MATCHING_ICCID,
+                MATCHING_PHONE_NUMBER,
+                MATCHING_CARRIER_ID,
+                "us",
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE);
+
+        TEST_SIM_INFO_VALUES_FR = populateContentValues(
+                MATCHING_ICCID,
+                MATCHING_PHONE_NUMBER,
+                MATCHING_CARRIER_ID,
+                "fr",
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE);
+
+        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_ICCID = populateContentValues(
+                MATCHING_ICCID,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_1,
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                null,
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_1);
+
+        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_NUMBER_AND_CID = populateContentValues(
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_2,
+                MATCHING_PHONE_NUMBER,
+                MATCHING_CARRIER_ID,
+                null,
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_2);
+
+        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID = populateContentValues(
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_3,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_3,
+                MATCHING_CARRIER_ID,
+                null,
+                ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3,
+                ARBITRARY_SIMINFO_DB_TEST_STRING_VALUE_3);
+    }
+
+    private static ContentValues populateContentValues(
+            String iccId, String phoneNumber, int carrierId, String isoCountryCode,
+            int arbitraryIntVal, String arbitraryStringVal) {
+            ContentValues contentValues = new ContentValues();
+
+        contentValues.put(Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_ICC_ID, iccId);
+        contentValues.put(Telephony.SimInfo.COLUMN_NUMBER, phoneNumber);
+        contentValues.put(Telephony.SimInfo.COLUMN_CARD_ID, arbitraryStringVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_CARRIER_ID, carrierId);
+        contentValues.put(Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_VT_IMS_ENABLED, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_MODE, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                arbitraryStringVal);
+        if (isoCountryCode != null) {
+            contentValues.put(Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE, isoCountryCode);
+        }
+
+        return contentValues;
+    }
+
     /**
      * This is used to give the TelephonyProviderTest a mocked context which takes a
      * TelephonyProvider and attaches it to the ContentResolver with telephony authority.
@@ -123,12 +226,14 @@
     private class MockContextWithProvider extends MockContext {
         private final MockContentResolver mResolver;
         private TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+        private SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
 
         private final List<String> GRANTED_PERMISSIONS = Arrays.asList(
                 Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.WRITE_APN_SETTINGS,
                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
 
-        public MockContextWithProvider(TelephonyProvider telephonyProvider) {
+        public MockContextWithProvider(TelephonyProvider telephonyProvider,
+                Boolean isActiveSubscription) {
             mResolver = new MockContentResolver() {
                 @Override
                 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
@@ -146,7 +251,8 @@
 
             // return test subId 0 for all operators
             doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator(anyInt());
-
+            doReturn(isActiveSubscription).when(mSubscriptionManager)
+                    .isActiveSubscriptionId(anyInt());
             doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
             doReturn(TEST_OPERATOR).when(mTelephonyManager).getSimOperator();
             doReturn(TEST_CARRIERID).when(mTelephonyManager).getSimCarrierId();
@@ -171,6 +277,9 @@
             if (name.equals(Context.TELEPHONY_SERVICE)) {
                 Log.d(TAG, "getSystemService: returning mock TM");
                 return mTelephonyManager;
+            } else if (name.equals(Context.TELEPHONY_SUBSCRIPTION_SERVICE)){
+                Log.d(TAG, "getSystemService: returning mock SubscriptionManager");
+                return mSubscriptionManager;
             } else {
                 Log.d(TAG, "getSystemService: returning null");
                 return null;
@@ -181,6 +290,8 @@
         public String getSystemServiceName(Class<?> serviceClass) {
             if (serviceClass.equals(TelephonyManager.class)) {
               return Context.TELEPHONY_SERVICE;
+            } else if (serviceClass.equals(SubscriptionManager.class)) {
+                return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
             } else {
                 Log.d(TAG, "getSystemServiceName: returning null");
                 return null;
@@ -189,8 +300,7 @@
 
         @Override
         public Resources getResources() {
-            Log.d(TAG, "getResources: returning null");
-            return null;
+            return mockContextResources;
         }
 
         @Override
@@ -216,6 +326,20 @@
                 return PackageManager.PERMISSION_DENIED;
             }
         }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            if (permission == android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
+                    || permission == android.Manifest.permission.MODIFY_PHONE_STATE) {
+                return;
+            }
+            throw new SecurityException("Unavailable permission requested");
+        }
+
+        @Override
+        public File getFilesDir() {
+            return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+        }
     }
 
     @Override
@@ -223,16 +347,27 @@
         super.setUp();
         MockitoAnnotations.initMocks(this);
         mTelephonyProviderTestable = new TelephonyProviderTestable();
-        mContext = new MockContextWithProvider(mTelephonyProviderTestable);
-        mContentResolver = (MockContentResolver) mContext.getContentResolver();
+        when(mockContextResources.getStringArray(anyInt())).thenReturn(new String[]{"ca", "us"});
         notifyChangeCount = 0;
         notifyChangeRestoreCount = 0;
     }
 
+    private void setUpMockContext(boolean isActiveSubId) {
+        mContext = new MockContextWithProvider(mTelephonyProviderTestable, isActiveSubId);
+        mContentResolver = mContext.getContentResolver();
+    }
+
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
         mTelephonyProviderTestable.closeDatabase();
+
+        // Remove the internal file created by SIM-specific settings restore
+        File file = new File(mContext.getFilesDir(),
+                mTelephonyProviderTestable.BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE);
+        if (file.exists()) {
+            file.delete();
+        }
     }
 
     /**
@@ -242,6 +377,8 @@
     @Test
     @SmallTest
     public void testBulkInsertCarriers() {
+        setUpMockContext(true);
+
         // insert 2 test contentValues
         ContentValues contentValues = new ContentValues();
         final String insertApn = "exampleApnName";
@@ -313,6 +450,8 @@
     @Test
     @SmallTest
     public void testMccMncMigration() {
+        setUpMockContext(true);
+
         CarrierIdProviderTestable carrierIdProvider = new CarrierIdProviderTestable();
         carrierIdProvider.initializeForTesting(mContext);
         mContentResolver.addProvider(Telephony.CarrierId.All.CONTENT_URI.getAuthority(),
@@ -372,6 +511,8 @@
     @Test
     @SmallTest
     public void testUpdateConflictingCarriers() {
+        setUpMockContext(true);
+
         // insert 2 test contentValues
         ContentValues contentValues = new ContentValues();
         final String insertApn = "exampleApnName";
@@ -429,6 +570,8 @@
     }
 
     private void doSimpleTestForUri(Uri uri) {
+        setUpMockContext(true);
+
         // insert test contentValues
         ContentValues contentValues = new ContentValues();
         final String insertApn = "exampleApnName";
@@ -479,6 +622,8 @@
     @Test
     @SmallTest
     public void testOwnedBy() {
+        setUpMockContext(true);
+
         // insert test contentValues
         ContentValues contentValues = new ContentValues();
         final String insertApn = "exampleApnName";
@@ -542,6 +687,8 @@
     @Test
     @SmallTest
     public void testSimTable() {
+        setUpMockContext(true);
+
         // insert test contentValues
         ContentValues contentValues = new ContentValues();
         final int insertSubId = 11;
@@ -603,6 +750,222 @@
         assertEquals(0, cursor.getCount());
     }
 
+    @Test
+    public void testFullRestoreOnMatchingIccId() {
+        byte[] simSpecificSettingsData = getBackupData(
+                new ContentValues[]{
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_ICCID,
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_NUMBER_AND_CID,
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID});
+        createInternalBackupFile(simSpecificSettingsData);
+        mContentResolver.insert(SubscriptionManager.CONTENT_URI, TEST_SIM_INFO_VALUES_US);
+
+        mContext.getContentResolver().call(
+                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                MATCHING_ICCID, null);
+
+        Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+                null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+
+        // Make sure SubId didn't get overridden.
+        assertEquals(
+                (int)TEST_SIM_INFO_VALUES_US.getAsInteger(
+                        Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID),
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        // Ensure all other values got updated.
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_VT_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_MODE));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE));
+
+        assertRestoredSubIdIsRemembered();
+    }
+
+    @Test
+    public void testFullRestoreOnMatchingNumberAndCid() {
+        byte[] simSpecificSettingsData = getBackupData(
+                new ContentValues[]{
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_NUMBER_AND_CID,
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID});
+        createInternalBackupFile(simSpecificSettingsData);
+        mContentResolver.insert(SubscriptionManager.CONTENT_URI, TEST_SIM_INFO_VALUES_US);
+
+        mContext.getContentResolver().call(
+                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                MATCHING_ICCID, null);
+
+        Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+                null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+
+        // Make sure SubId didn't get overridden.
+        assertEquals(
+                (int) TEST_SIM_INFO_VALUES_US.getAsInteger(
+                        Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID),
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        // Ensure all other values got updated.
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_VT_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_MODE));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_2,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE));
+
+        assertRestoredSubIdIsRemembered();
+    }
+
+    @Test
+    public void testFullRestoreOnMatchingCidOnly() {
+        byte[] simSpecificSettingsData = getBackupData(
+                new ContentValues[]{
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID});
+        createInternalBackupFile(simSpecificSettingsData);
+        mContentResolver.insert(SubscriptionManager.CONTENT_URI, TEST_SIM_INFO_VALUES_US);
+
+        mContext.getContentResolver().call(
+                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                MATCHING_ICCID, null);
+
+        Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+                null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+
+        // Make sure SubId didn't get overridden.
+        assertEquals(
+                (int) TEST_SIM_INFO_VALUES_US.getAsInteger(
+                        Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID),
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        // Ensure sensitive settings did not get updated.
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
+        // Ensure all other values got updated.
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_VT_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_MODE));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_3,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE));
+
+        assertRestoredSubIdIsRemembered();
+    }
+
+    @Test
+    public void testFullRestoreOnMatchingIccIdWithFranceISO() {
+        byte[] simSpecificSettingsData = getBackupData(
+                new ContentValues[]{
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_ICCID,
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_NUMBER_AND_CID,
+                        BACKED_UP_SIM_INFO_VALUES_WITH_MATCHING_CID});
+        createInternalBackupFile(simSpecificSettingsData);
+        mContentResolver.insert(SubscriptionManager.CONTENT_URI, TEST_SIM_INFO_VALUES_FR);
+
+        mContext.getContentResolver().call(
+                SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                MATCHING_ICCID, null);
+
+        Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+                null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+
+        // Make sure SubId didn't get overridden.
+        assertEquals(
+                (int) TEST_SIM_INFO_VALUES_FR.getAsInteger(
+                        Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID),
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID));
+        // Ensure all other values got updated.
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_VT_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_MODE));
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE_1,
+                getIntValueFromCursor(cursor, Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE));
+
+        assertRestoredSubIdIsRemembered();
+    }
+
+    private void assertRestoredSubIdIsRemembered() {
+        PersistableBundle bundle = getPersistableBundleFromInternalStorageFile();
+        int[] previouslyRestoredSubIds =
+                bundle.getIntArray(TelephonyProvider.KEY_PREVIOUSLY_RESTORED_SUB_IDS);
+        assertNotNull(previouslyRestoredSubIds);
+        assertEquals(ARBITRARY_SIMINFO_DB_TEST_INT_VALUE, previouslyRestoredSubIds[0]);
+    }
+
+    private PersistableBundle getPersistableBundleFromInternalStorageFile() {
+        File file = new File(Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_DOWNLOADS),
+                TelephonyProvider.BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE);
+        try (FileInputStream fis = new FileInputStream(file)) {
+            return PersistableBundle.readFromStream(fis);
+        } catch (IOException e) {
+        }
+
+        return null;
+    }
+
+    private byte[] getBackupData(ContentValues[] contentValues) {
+        setUpMockContext(true);
+
+        int rowsAdded = mContentResolver.bulkInsert(SubscriptionManager.CONTENT_URI, contentValues);
+        assertEquals(rowsAdded, contentValues.length);
+
+        Cursor cursor = mContentResolver.query(SubscriptionManager.CONTENT_URI,
+            null, null, null, null);
+        assertEquals(cursor.getCount(), contentValues.length);
+
+        Bundle bundle =  mContext.getContentResolver().call(
+            SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+            SubscriptionManager.GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+        byte[] data = bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+
+        int rowsDeleted = mContentResolver.delete(SubscriptionManager.CONTENT_URI, null, null);
+        assertEquals(rowsDeleted, contentValues.length);
+
+        return data;
+    }
+
+    private void createInternalBackupFile(byte[] data) {
+        mTelephonyProviderTestable.writeSimSettingsToInternalStorage(data);
+    }
+
+    private int getIntValueFromCursor(Cursor cursor, String columnName) {
+        int columnIndex = cursor.getColumnIndex(columnName);
+        return cursor.getInt(columnIndex);
+    }
+
     private int parseIdFromInsertedUri(Uri uri) throws NumberFormatException {
         return (uri != null) ? Integer.parseInt(uri.getLastPathSegment()) : -1;
     }
@@ -627,6 +990,8 @@
     @Test
     @SmallTest
     public void testEnforceManagedUri() {
+        setUpMockContext(true);
+
         mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
 
         final int current = 1;
@@ -749,6 +1114,8 @@
      * Test URL_TELEPHONY cannot insert, query, update or delete DPC records.
      */
     public void testTelephonyUriDpcRecordAccessControl() {
+        setUpMockContext(true);
+
         mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
 
         final int current = 1;
@@ -825,6 +1192,8 @@
     @Test
     @SmallTest
     public void testDpcUri() {
+        setUpMockContext(true);
+
         int dpcRecordId = 0, othersRecordId = 0;
         try {
             mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
@@ -916,6 +1285,8 @@
     @Test
     @SmallTest
     public void testDpcUriOnConflict() {
+        setUpMockContext(true);
+
         int dpcRecordId1 = 0, dpcRecordId2 = 0;
         try {
             mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID);
@@ -988,6 +1359,8 @@
     @Test
     @SmallTest
     public void testAccessUrlDpcThrowSecurityExceptionFromOtherUid() {
+        setUpMockContext(true);
+
         mTelephonyProviderTestable.fakeCallingUid(Process.SYSTEM_UID + 123456);
 
         // Test insert().
@@ -1091,6 +1464,8 @@
     }
 
     private void preserveEditedValueInMerge(int value) {
+        setUpMockContext(true);
+
         // insert user deleted APN
         String carrierName1 = "carrier1";
         String numeric1 = "123234";
@@ -1138,6 +1513,8 @@
     }
 
     private void preserveDeletedValueInMerge(int value) {
+        setUpMockContext(true);
+
         // insert user deleted APN
         String carrierName1 = "carrier1";
         String numeric1 = "123234";
@@ -1184,6 +1561,8 @@
     @Test
     @SmallTest
     public void testQueryPreferredApn() {
+        setUpMockContext(true);
+
         // create APNs
         ContentValues preferredValues = new ContentValues();
         final String preferredApn = "preferredApn";
@@ -1230,6 +1609,8 @@
     @Test
     @SmallTest
     public void testApnSetId() {
+        setUpMockContext(true);
+
         // create APNs
         ContentValues values1 = new ContentValues();
         final String apn = "apnName";
@@ -1274,6 +1655,8 @@
     @Test
     @SmallTest
     public void testPreferApnSetUrl() {
+        setUpMockContext(true);
+
         // create APNs
         ContentValues values1 = new ContentValues();
         final String apn = "apnName";
@@ -1298,20 +1681,35 @@
         values3.put(Carriers.NUMERIC, TEST_OPERATOR);
         values3.put(Carriers.APN_SET_ID, 1);
 
+        // values4 has a matching setId but it belongs to a different carrier
+        ContentValues values4 = new ContentValues();
+        final String apn4 = "fourthApnName";
+        final String name4 = "name4";
+        values4.put(Carriers.APN, apn4);
+        values4.put(Carriers.NAME, name4);
+        values4.put(Carriers.NUMERIC, "999888");
+        values4.put(Carriers.APN_SET_ID, 1);
+
         // insert APNs
         // we explicitly include subid, as SubscriptionManager.getDefaultSubscriptionId() returns -1
         Log.d(TAG, "testPreferApnSetUrl: inserting contentValues=" + values1 + ", " + values2
-                + ", " + values3);
+                + ", " + values3 + ", " + values4);
         mContentResolver.insert(CONTENT_URI_WITH_SUBID, values1);
         mContentResolver.insert(CONTENT_URI_WITH_SUBID, values2);
+        mContentResolver.insert(CONTENT_URI_WITH_SUBID, values4);
         Uri uri = mContentResolver.insert(CONTENT_URI_WITH_SUBID, values3);
 
-        // before there's a preferred APN set, assert that all APNs are returned
+        // verify all APNs were correctly inserted
         final String[] testProjection = { Carriers.NAME };
         Cursor cursor = mContentResolver.query(
+                Carriers.CONTENT_URI, testProjection, null, null, null);
+        assertEquals(4, cursor.getCount());
+
+        // preferapnset/subId returns null when there is no preferred APN
+        cursor = mContentResolver.query(
                 Uri.withAppendedPath(Carriers.CONTENT_URI, "preferapnset/subId/" + TEST_SUBID),
                 testProjection, null, null, null);
-        assertEquals(3, cursor.getCount());
+        assertNull(cursor);
 
         // set the APN from values3 (apn_set_id = 1) to the preferred APN
         final String preferredApnIdString = uri.getLastPathSegment();
@@ -1326,6 +1724,7 @@
         cursor = mContentResolver.query(
                 Uri.withAppendedPath(Carriers.CONTENT_URI, "preferapnset/subId/" + TEST_SUBID),
                 testProjection, null, null, null);
+        // values4 which was inserted with a different carrier is not included in the results
         assertEquals(2, cursor.getCount());
         cursor.moveToFirst();
         assertEquals(name2, cursor.getString(0));
@@ -1339,6 +1738,8 @@
     @Test
     @SmallTest
     public void testRestoreDefaultApn() {
+        setUpMockContext(true);
+
         // setup for multi-SIM
         TelephonyManager telephonyManager =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -1435,6 +1836,8 @@
     @Test
     @SmallTest
     public void testUpdateWfcEnabled() {
+        setUpMockContext(true);
+
         // insert test contentValues
         ContentValues contentValues = new ContentValues();
         final int insertSubId = 1;
@@ -1481,6 +1884,8 @@
     @Test
     @SmallTest
     public void testSIMAPNLIST_MatchTheMVNOAPN() {
+        setUpMockContext(true);
+
         // Test on getSubscriptionMatchingAPNList() step 1
         final String apnName = "apnName";
         final String carrierName = "name";
@@ -1519,6 +1924,7 @@
                         Carriers.NUMERIC,
                         Carriers.MVNO_MATCH_DATA
                 };
+
         Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
                 testProjection, null, null, null);
 
@@ -1534,6 +1940,8 @@
     @Test
     @SmallTest
     public void testSIMAPNLIST_MatchTheMNOAPN() {
+        setUpMockContext(true);
+
         // Test on getSubscriptionMatchingAPNList() step 2
         final String apnName = "apnName";
         final String carrierName = "name";
@@ -1553,6 +1961,7 @@
                         Carriers.NAME,
                         Carriers.NUMERIC,
                 };
+
         Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
                 testProjection, null, null, null);
 
@@ -1565,6 +1974,8 @@
     @Test
     @SmallTest
     public void testSIMAPNLIST_MatchTheCarrierIDANDMNOAPN() {
+        setUpMockContext(true);
+
         // Test on getSubscriptionMatchingAPNList() will return the {MCCMNC}
         final String apnName = "apnName";
         final String carrierName = "name";
@@ -1592,6 +2003,7 @@
                 Carriers.NAME,
                 Carriers.CARRIER_ID,
             };
+
         Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST, testProjection, null, null, null);
 
         // The query based on SIM_APN_LIST will return MNO APN and the APN that has carrier id
@@ -1601,6 +2013,8 @@
     @Test
     @SmallTest
     public void testSIMAPNLIST_MatchTheCarrierAPNAndMVNOAPN() {
+        setUpMockContext(true);
+
         final String apnName = "apnName";
         final String carrierName = "name";
         final String mvnoType = "spn";
@@ -1638,6 +2052,7 @@
                 Carriers.CARRIER_ID,
                 Carriers.MVNO_TYPE,
             };
+
         Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
             testProjection, null, null, null);
 
@@ -1648,4 +2063,34 @@
                     || !TextUtils.isEmpty(cursor.getString(3)));
         }
     }
+
+    @Test
+    @SmallTest
+    public void testSIMAPNLIST_isNotActiveSubscription() {
+        setUpMockContext(false);
+
+        // Test on getSubscriptionMatchingAPNList() step 2
+        final String apnName = "apnName";
+        final String carrierName = "name";
+        final String numeric = TEST_OPERATOR;
+
+        // Insert the MNO APN
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Carriers.APN, apnName);
+        contentValues.put(Carriers.NAME, carrierName);
+        contentValues.put(Carriers.NUMERIC, numeric);
+        mContentResolver.insert(Carriers.CONTENT_URI, contentValues);
+
+        // Query DB
+        final String[] testProjection =
+                {
+                        Carriers.APN,
+                        Carriers.NAME,
+                        Carriers.NUMERIC,
+                };
+        Cursor cursor = mContentResolver.query(URL_SIM_APN_LIST,
+                testProjection, null, null, null);
+
+        assertNull(cursor);
+    }
 }