[automerger skipped] Merge "Merge Android 12L" am: e09af8e595 -s ours am: a4ce5ddded -s ours am: 4e4b172338 -s ours

am skip reason: Merged-In I5c6b7898864fe125c9a274be800ad237d6e42738 with SHA-1 96eee697d6 is already in history

Original change: https://android-review.googlesource.com/c/platform/packages/providers/TelephonyProvider/+/2012246

Change-Id: I55777db3e7d9ac08cc56c528c922022898bc653b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0a9802d..6633d14 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,6 +19,10 @@
         coreApp="true"
         android:sharedUserId="android.uid.phone">
 
+    <permission android:name="android.permission.ACCESS_TELEPHONY_SIMINFO_DB"
+                android:label="Read and write SIMINFO table in TelephonyProvider"
+                android:protectionLevel="signature" />
+
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 6994d18..20ad0b0 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -17,5 +17,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" product="tablet" msgid="9194799012395299737">"ম’বাইল নেটৱৰ্ক কনফিগাৰেশ্বন"</string>
-    <string name="app_label" product="default" msgid="8338087656149558019">"ফ\'ন আৰু বাৰ্তাৰ সঞ্চয়াগাৰ"</string>
+    <string name="app_label" product="default" msgid="8338087656149558019">"ফ’ন আৰু বাৰ্তাৰ ষ্ট’ৰেজ"</string>
 </resources>
diff --git a/src/com/android/providers/telephony/CarrierDatabaseHelper.java b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
index 0b97da1..3d2de29 100644
--- a/src/com/android/providers/telephony/CarrierDatabaseHelper.java
+++ b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
@@ -28,14 +28,13 @@
 
 public class CarrierDatabaseHelper extends SQLiteOpenHelper {
     private static final String TAG = "CarrierDatabaseHelper";
-    private static final boolean DBG = true;
-
     private static final String DATABASE_NAME = "CarrierInformation.db";
     public static final String CARRIER_KEY_TABLE = "carrier_key";
-    private static final int DATABASE_VERSION = 2;
+    private static final int DATABASE_VERSION = 3;
 
     /**
      * CarrierDatabaseHelper carrier database helper class.
+     *
      * @param context of the user.
      */
     public CarrierDatabaseHelper(Context context) {
@@ -46,21 +45,20 @@
     public static final String KEY_TYPE = "key_type";
     public static final String MCC = "mcc";
     public static final String MNC = "mnc";
-    public static final String MVNO_TYPE = "mvno_type";
-    public static final String MVNO_MATCH_DATA = "mvno_match_data";
+    public static final String CARRIER_ID = "carrier_id";
     public static final String PUBLIC_KEY = "public_key";
     public static final String KEY_IDENTIFIER = "key_identifier";
     public static final String EXPIRATION_TIME = "expiration_time";
     public static final String LAST_MODIFIED = "last_modified";
 
     private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
+    private static final String TEMPORARY_CARRIER_KEY_TABLE = CARRIER_KEY_TABLE + "_temp";
 
     static {
         CARRIERS_UNIQUE_FIELDS.add(MCC);
         CARRIERS_UNIQUE_FIELDS.add(MNC);
+        CARRIERS_UNIQUE_FIELDS.add(CARRIER_ID);
         CARRIERS_UNIQUE_FIELDS.add(KEY_TYPE);
-        CARRIERS_UNIQUE_FIELDS.add(MVNO_TYPE);
-        CARRIERS_UNIQUE_FIELDS.add(MVNO_MATCH_DATA);
     }
 
     public static String getStringForCarrierKeyTableCreation(String tableName) {
@@ -68,8 +66,7 @@
                 "(_id INTEGER PRIMARY KEY," +
                 MCC + " TEXT DEFAULT ''," +
                 MNC + " TEXT DEFAULT ''," +
-                MVNO_TYPE + " TEXT DEFAULT ''," +
-                MVNO_MATCH_DATA + " TEXT DEFAULT ''," +
+                CARRIER_ID + " INTEGER DEFAULT -1," +
                 KEY_TYPE + " TEXT DEFAULT ''," +
                 KEY_IDENTIFIER + " TEXT DEFAULT ''," +
                 PUBLIC_KEY + " BLOB DEFAULT ''," +
@@ -93,10 +90,43 @@
 
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        Log.d(TAG, "dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
+        Log.d(TAG, "dbh.onUpgrade:db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
         if (oldVersion < 2) {
             dropCarrierTable(db);
             createCarrierTable(db);
+            return;
+        }
+        if (oldVersion < 3) {
+            // Create new table and copy the contents from the existing table
+            renameCarrierTable(db);
+            createCarrierTable(db);
+            copyContents(db);
+            dropTemporaryTable(db);
+            db.execSQL("COMMIT");
+            return;
         }
     }
-}
+
+    // Renames the existing table as temporary table
+    private void renameCarrierTable(SQLiteDatabase db) {
+        db.execSQL(
+                "ALTER TABLE " + CARRIER_KEY_TABLE + " RENAME TO " + TEMPORARY_CARRIER_KEY_TABLE);
+    }
+
+    // Copies the content from temporary table to new table
+    private void copyContents(SQLiteDatabase db) {
+        String copyStr = new StringBuilder().append("INSERT INTO ").append(
+                CARRIER_KEY_TABLE).append(
+                " (MCC, MNC, KEY_TYPE, KEY_IDENTIFIER, PUBLIC_KEY, "
+                        + "EXPIRATION_TIME, LAST_MODIFIED)").append(" SELECT ").append(
+                "MCC, MNC, KEY_TYPE, KEY_IDENTIFIER, PUBLIC_KEY, "
+                        + "EXPIRATION_TIME, LAST_MODIFIED").append(" FROM ").append(
+                TEMPORARY_CARRIER_KEY_TABLE).toString();
+        db.execSQL(copyStr);
+    }
+
+    // Drops the temporary table
+    private void dropTemporaryTable(SQLiteDatabase db) {
+        db.execSQL("DROP TABLE IF EXISTS " + TEMPORARY_CARRIER_KEY_TABLE + ";");
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 96059d7..b5f8ce0 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -98,8 +98,13 @@
         // or received messages, without wap pushes.
         final boolean accessRestricted = ProviderUtil.isAccessRestricted(
                 getContext(), getCallingPackage(), Binder.getCallingUid());
-        final String pduTable = getPduTable(accessRestricted);
 
+        // If access is restricted, we don't allow subqueries in the query.
+        if (accessRestricted) {
+            SqlQueryChecker.checkQueryParametersForSubqueries(projection, selection, sortOrder);
+        }
+
+        final String pduTable = getPduTable(accessRestricted);
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
 
         // Generate the body of the query.
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index a9494fb..73ffa3b 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -295,7 +295,7 @@
         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
         setWriteAheadLoggingEnabled(false);
         try {
-            PhoneFactory.addLocalLog(TAG, 100);
+            PhoneFactory.addLocalLog(TAG, 64);
         } catch (IllegalArgumentException e) {
             // ignore
         }
@@ -1657,7 +1657,7 @@
             } finally {
                 db.endTransaction();
             }
-            // fall through
+            return;
         }
 
         Log.e(TAG, "Destroying all old data.");
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index be42ab3..d643d00 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -159,7 +159,7 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private static final int DATABASE_VERSION = 55 << 16;
+    private static final int DATABASE_VERSION = 57 << 16;
     private static final int URL_UNKNOWN = 0;
     private static final int URL_TELEPHONY = 1;
     private static final int URL_CURRENT = 2;
@@ -435,16 +435,19 @@
         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_D2D_STATUS_SHARING_SELECTED_CONTACTS,
+                Cursor.FIELD_TYPE_STRING);
+        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);
-        SIM_INFO_COLUMNS_TO_BACKUP.put(
                 Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, Cursor.FIELD_TYPE_INTEGER);
+        SIM_INFO_COLUMNS_TO_BACKUP.put(
+                Telephony.SimInfo.COLUMN_USAGE_SETTING,
+                Cursor.FIELD_TYPE_INTEGER);
     }
 
     @VisibleForTesting
@@ -571,12 +574,15 @@
                 + 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_VOIMS_OPT_IN_STATUS + " INTEGER DEFAULT 0,"
                 + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS + " TEXT,"
                 + Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED + " INTEGER DEFAULT -1,"
                 + Telephony.SimInfo.COLUMN_PHONE_NUMBER_SOURCE_CARRIER + " TEXT,"
-                + Telephony.SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS + " TEXT"
+                + Telephony.SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS + " TEXT,"
+                + Telephony.SimInfo.COLUMN_PORT_INDEX + "  INTEGER DEFAULT -1,"
+                + Telephony.SimInfo.COLUMN_USAGE_SETTING + " INTEGER DEFAULT "
+                + SubscriptionManager.USAGE_SETTING_UNKNOWN
                 + ");";
     }
 
@@ -1660,21 +1666,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) {
@@ -1683,6 +1674,20 @@
                                 + " to add d2d status sharing column. ");
                     }
                 }
+            }
+
+            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_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 = 50 << 16 | 6;
             }
 
@@ -1704,7 +1709,7 @@
             if (oldVersion < (52 << 16 | 6)) {
                 try {
                     // Try to update the siminfo table. It might not be there.
-                    db.execSQL("ALERT TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
                             + Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED
                             + " INTEGER DEFAULT -1;");
                 } catch (SQLiteException e) {
@@ -1774,6 +1779,37 @@
                 oldVersion = 55 << 16 | 6;
             }
 
+            if (oldVersion < (56 << 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_PORT_INDEX
+                            + " INTEGER DEFAULT -1;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 56 << 16 | 6;
+            }
+
+            if (oldVersion < (57 << 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_USAGE_SETTING
+                            + " INTEGER DEFAULT " + SubscriptionManager.USAGE_SETTING_UNKNOWN
+                            + ";");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade failed to updated " + SIMINFO_TABLE
+                                + " to add preferred usage setting");
+                    }
+                }
+                oldVersion = 57 << 16 | 6;
+            }
+
             if (DBG) {
                 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
             }
@@ -2994,7 +3030,7 @@
         mOpenHelper = new DatabaseHelper(getContext());
 
         try {
-            PhoneFactory.addLocalLog(TAG, 100);
+            PhoneFactory.addLocalLog(TAG, 64);
         } catch (IllegalArgumentException e) {
             // ignore
         }
@@ -3231,8 +3267,10 @@
     }
 
     boolean isCallingFromSystemOrPhoneUid() {
-        return mInjector.binderGetCallingUid() == Process.SYSTEM_UID ||
-                mInjector.binderGetCallingUid() == Process.PHONE_UID;
+        int callingUid = mInjector.binderGetCallingUid();
+        return callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
+                // Allow ROOT for testing. ROOT can access underlying DB files anyways.
+                || callingUid == Process.ROOT_UID;
     }
 
     void ensureCallingFromSystemOrPhoneUid(String message) {
@@ -3603,7 +3641,7 @@
                 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion,
                 String isoCountryCodeFromDb,
                 List<String> wfcRestoreBlockedCountries) {
-            if (DATABASE_VERSION != 55 << 16) {
+            if (DATABASE_VERSION != 57 << 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 "
@@ -3645,6 +3683,12 @@
              * Also make sure to add necessary removal of sensitive settings in
              * polishContentValues(ContentValues contentValues).
              */
+            if (backupDataFormatVersion >= 57 << 16) {
+                contentValues.put(Telephony.SimInfo.COLUMN_USAGE_SETTING,
+                        backedUpSimInfoEntry.getInt(
+                                Telephony.SimInfo.COLUMN_USAGE_SETTING,
+                                SubscriptionManager.USAGE_SETTING_UNKNOWN));
+            }
             if (backupDataFormatVersion >= 52 << 16) {
                 contentValues.put(Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
                         backedUpSimInfoEntry.getInt(
@@ -4287,12 +4331,12 @@
         Uri result = null;
         int subId = SubscriptionManager.getDefaultSubscriptionId();
 
-        checkPermission();
+        int match = s_urlMatcher.match(url);
+        checkPermission(match);
         syncBearerBitmaskAndNetworkTypeBitmask(initialValues);
 
         boolean notify = false;
         SQLiteDatabase db = getWritableDatabase();
-        int match = s_urlMatcher.match(url);
         switch (match)
         {
             case URL_TELEPHONY_USING_SUBID:
@@ -4440,10 +4484,10 @@
         ContentValues cv = new ContentValues();
         cv.put(EDITED_STATUS, USER_DELETED);
 
-        checkPermission();
+        int match = s_urlMatcher.match(url);
+        checkPermission(match);
 
         SQLiteDatabase db = getWritableDatabase();
-        int match = s_urlMatcher.match(url);
         switch (match)
         {
             case URL_DELETE:
@@ -4602,11 +4646,11 @@
         int uriType = URL_UNKNOWN;
         int subId = SubscriptionManager.getDefaultSubscriptionId();
 
-        checkPermission();
+        int match = s_urlMatcher.match(url);
+        checkPermission(match);
         syncBearerBitmaskAndNetworkTypeBitmask(values);
 
         SQLiteDatabase db = getWritableDatabase();
-        int match = s_urlMatcher.match(url);
         switch (match)
         {
             case URL_TELEPHONY_USING_SUBID:
@@ -4880,6 +4924,12 @@
                                         Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED),
                                 usingSubId, subId), null, true, UserHandle.USER_ALL);
                     }
+                    if (values.containsKey(Telephony.SimInfo.COLUMN_USAGE_SETTING)) {
+                        getContext().getContentResolver().notifyChange(getNotifyContentUri(
+                                Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+                                        Telephony.SimInfo.COLUMN_USAGE_SETTING),
+                                usingSubId, subId), null, true, UserHandle.USER_ALL);
+                    }
                     break;
                 default:
                     getContext().getContentResolver().notifyChange(
@@ -4894,7 +4944,28 @@
         return (usingSubId) ? Uri.withAppendedPath(uri, "" + subId) : uri;
     }
 
-    private void checkPermission() {
+    /**
+     * Checks permission to query or insert/update/delete the database. The permissions required
+     * for APN DB and SIMINFO DB are different:
+     * <ul>
+     * <li>APN DB requires WRITE_APN_SETTINGS or carrier privileges
+     * <li>SIMINFO DB requires phone UID; it's for phone internal usage only
+     * </ul>
+     */
+    private void checkPermission(int match) {
+        switch (match) {
+            case URL_SIMINFO:
+            case URL_SIMINFO_USING_SUBID:
+            case URL_SIMINFO_SUW_RESTORE:
+            case URL_SIMINFO_SIM_INSERTED_RESTORE:
+                checkPermissionForSimInfoTable();
+                break;
+            default:
+                checkPermissionForApnTable();
+        }
+    }
+
+    private void checkPermissionForApnTable() {
         int status = getContext().checkCallingOrSelfPermission(
                 "android.permission.WRITE_APN_SETTINGS");
         if (status == PackageManager.PERMISSION_GRANTED) {
@@ -4935,12 +5006,14 @@
             log("Using old permission behavior for telephony provider compat");
             checkQueryPermission(match, projectionIn);
         } else {
-            checkPermission();
+            checkPermission(match);
         }
     }
 
     private void checkQueryPermission(int match, String[] projectionIn) {
-        if (match != URL_SIMINFO) {
+        if (match == URL_SIMINFO) {
+            checkPermissionForSimInfoTable();
+        } else {
             if (projectionIn != null) {
                 for (String column : projectionIn) {
                     if (TYPE.equals(column) ||
@@ -4952,17 +5025,27 @@
                             APN.equals(column)) {
                         // noop
                     } else {
-                        checkPermission();
+                        checkPermissionForApnTable();
                         break;
                     }
                 }
             } else {
                 // null returns all columns, so need permission check
-                checkPermission();
+                checkPermissionForApnTable();
             }
         }
     }
 
+    private void checkPermissionForSimInfoTable() {
+        ensureCallingFromSystemOrPhoneUid("Access SIMINFO table from not phone/system UID");
+        if (getContext().checkCallingOrSelfPermission(
+                    "android.permission.ACCESS_TELEPHONY_SIMINFO_DB")
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        throw new SecurityException("No permission to access SIMINFO table");
+    }
+
     private DatabaseHelper mOpenHelper;
 
     private void restoreDefaultAPN(int subId) {
diff --git a/tests/src/com/android/providers/telephony/CarrierProviderTest.java b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
index eb95fb6..d329002 100644
--- a/tests/src/com/android/providers/telephony/CarrierProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
@@ -22,14 +22,13 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.providers.telephony.CarrierProvider;
-
 import junit.framework.TestCase;
 
 import org.junit.Test;
@@ -60,11 +59,10 @@
     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;
-
+    public static final int TEST_CARRIER_ID_1 = 1;
+    public static final int TEST_CARRIER_ID_2 = 2;
 
     /**
      * This is used to give the CarrierProviderTest a mocked context which takes a
@@ -147,8 +145,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         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);
@@ -183,8 +180,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         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);
@@ -199,8 +195,9 @@
             ContentValues updatedValues = new ContentValues();
             updatedValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key2);
             mContentResolver.update(CarrierProvider.CONTENT_URI, updatedValues,
-                    "mcc=? and mnc=? and key_type=?", new String[] { test_mcc, test_mnc,
-                            String.valueOf(test_type) });
+                    "mcc=? and mnc=? and carrier_id=? and key_type=?",
+                    new String[]{test_mcc, test_mnc, Integer.toString(TEST_CARRIER_ID_1),
+                            String.valueOf(test_type)});
         } catch (Exception e) {
             Log.d(TAG, "Error updating values:" + e);
         }
@@ -208,8 +205,9 @@
         try {
             String[] columns ={CarrierDatabaseHelper.PUBLIC_KEY};
             Cursor findEntry = mContentResolver.query(CarrierProvider.CONTENT_URI, columns,
-                    "mcc=? and mnc=? and key_type=?",
-                    new String[] { test_mcc, test_mnc, String.valueOf(test_type) }, null);
+                    "mcc=? and mnc=? and carrier_id=? and key_type=?",
+                    new String[]{test_mcc, test_mnc, Integer.toString(TEST_CARRIER_ID_1),
+                            String.valueOf(test_type)}, null);
             findEntry.moveToFirst();
             key = findEntry.getString(0);
         } catch (Exception e) {
@@ -229,8 +227,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
         contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
 
@@ -238,8 +235,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
         contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key2.getBytes());
 
@@ -265,6 +261,97 @@
     }
 
     /**
+     * Test inserting cert with same MCC and MNC but with diff carrier ID
+     */
+    @Test
+    @SmallTest
+    public void testMnoandMvnoCertificates() {
+        int count = -1;
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.CARRIER_ID, TEST_CARRIER_ID_1);
+        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, test_type);
+        contentValuesNew.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValuesNew.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValuesNew.put(CarrierDatabaseHelper.CARRIER_ID, TEST_CARRIER_ID_2);
+        contentValuesNew.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
+
+        try {
+            mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
+            mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValuesNew);
+        } catch (Exception e) {
+            System.out.println("Error inserting certificates:: " + e);
+        }
+
+        try {
+            Cursor countCursor = mContentResolver.query(CarrierProvider.CONTENT_URI,
+                    new String[]{"count(*) AS count"},
+                    null,
+                    null,
+                    null);
+            countCursor.moveToFirst();
+            count = countCursor.getInt(0);
+        } catch (Exception e) {
+            Log.d(TAG, "Exception in getting count:" + e);
+        }
+        assertEquals(2, count);
+    }
+
+    /**
+     * once upgrade to version 3, carrierId = -1
+     * After upgrade, it triggers for new download with correct carrierId
+     * This test case will test writing the new entry, even old entry for same
+     * operator is already existed
+     */
+    @Test
+    @SmallTest
+    public void testOldAndNewDBEntries() {
+        int count = -1;
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, test_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValues.put(CarrierDatabaseHelper.CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+        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, test_type);
+        contentValuesNew.put(CarrierDatabaseHelper.MCC, test_mcc);
+        contentValuesNew.put(CarrierDatabaseHelper.MNC, test_mnc);
+        contentValuesNew.put(CarrierDatabaseHelper.CARRIER_ID, TEST_CARRIER_ID_2);
+        contentValuesNew.put(CarrierDatabaseHelper.KEY_IDENTIFIER, test_key_identifier_data);
+        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
+
+        try {
+            mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
+            mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValuesNew);
+        } catch (Exception e) {
+            System.out.println("Error inserting certificates:: " + e);
+        }
+
+        try {
+            Cursor countCursor = mContentResolver.query(CarrierProvider.CONTENT_URI,
+                    new String[]{"count(*) AS count"},
+                    null,
+                    null,
+                    null);
+            countCursor.moveToFirst();
+            count = countCursor.getInt(0);
+        } catch (Exception e) {
+            Log.d(TAG, "Exception in getting count:" + e);
+        }
+        assertEquals(2, count);
+    }
+
+    /**
      * Test inserting duplicate values in carrier key table. Ensure that a SQLException is thrown.
      */
     @Test(expected = SQLException.class)
@@ -273,8 +360,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, test_key1.getBytes());
 
         try {
@@ -300,8 +386,7 @@
         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.CARRIER_ID, TEST_CARRIER_ID_1);
         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);
@@ -313,9 +398,11 @@
         }
 
         try {
-            String whereClause = "mcc=? and mnc=?";
-            String[] whereArgs = new String[] { test_mcc, test_mnc };
-            numRowsDeleted = mContentResolver.delete(CarrierProvider.CONTENT_URI, whereClause, whereArgs);
+            String whereClause = "mcc=? and mnc=? and carrier_id=?";
+            String[] whereArgs = new String[]{test_mcc, test_mnc, Integer.toString(
+                    TEST_CARRIER_ID_1)};
+            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/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
index bb80f03..9de138c 100644
--- a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -121,6 +121,19 @@
     }
 
     @Test
+    public void databaseHelperOnUpgrade_hasPortIndexField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasPortIndexField");
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the PORT_INDEX field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "port index columns: " + Arrays.toString(upgradedColumns));
+        assertTrue(Arrays.asList(upgradedColumns).contains(SubscriptionManager.PORT_INDEX));
+    }
+
+    @Test
     public void databaseHelperOnUpgrade_hasSkip464XlatField() {
         Log.d(TAG, "databaseHelperOnUpgrade_hasSkip464XlatField");
         // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
@@ -218,22 +231,6 @@
     }
 
     @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
@@ -250,6 +247,22 @@
     }
 
     @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_hasD2DSharingContactsField() {
         Log.d(TAG, "databaseHelperOnUpgrade_hasD2DSharingContactsField");
         // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
@@ -322,6 +335,22 @@
         assertTrue(Arrays.asList(columns).contains(Carriers.MTU_V6));
     }
 
+    @Test
+    public void databaseHelperOnUpgrade_hasUsageSettingField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasUsageSettingField");
+        // (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.USAGE_SETTING 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_USAGE_SETTING));
+    }
+
     /**
      * 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 3455ffd..b3892be 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -215,6 +215,7 @@
         contentValues.put(Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
                 arbitraryStringVal);
         contentValues.put(Telephony.SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED, arbitraryIntVal);
+        contentValues.put(Telephony.SimInfo.COLUMN_USAGE_SETTING, arbitraryIntVal);
         if (isoCountryCode != null) {
             contentValues.put(Telephony.SimInfo.COLUMN_ISO_COUNTRY_CODE, isoCountryCode);
         }
@@ -225,7 +226,7 @@
     /**
      * This is used to give the TelephonyProviderTest a mocked context which takes a
      * TelephonyProvider and attaches it to the ContentResolver with telephony authority.
-     * The mocked context also gives WRITE_APN_SETTINGS permissions
+     * The mocked context also gives permissions needed to access DB tables.
      */
     private class MockContextWithProvider extends MockContext {
         private final MockContentResolver mResolver;
@@ -234,7 +235,8 @@
 
         private final List<String> GRANTED_PERMISSIONS = Arrays.asList(
                 Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.WRITE_APN_SETTINGS,
-                Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                "android.permission.ACCESS_TELEPHONY_SIMINFO_DB");
 
         public MockContextWithProvider(TelephonyProvider telephonyProvider,
                 Boolean isActiveSubscription) {
@@ -354,6 +356,8 @@
         when(mockContextResources.getStringArray(anyInt())).thenReturn(new String[]{"ca", "us"});
         notifyChangeCount = 0;
         notifyChangeRestoreCount = 0;
+        // Required to access SIMINFO table
+        mTelephonyProviderTestable.fakeCallingUid(Process.PHONE_UID);
     }
 
     private void setUpMockContext(boolean isActiveSubId) {
@@ -701,12 +705,14 @@
         final String insertIccId = "exampleIccId";
         final String insertCardId = "exampleCardId";
         final int insertProfileClass = SubscriptionManager.PROFILE_CLASS_DEFAULT;
+        final int insertPortIndex = 1;
         contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
         contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
         contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
         contentValues.put(SubscriptionManager.ICC_ID, insertIccId);
         contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
         contentValues.put(SubscriptionManager.PROFILE_CLASS, insertProfileClass);
+        contentValues.put(SubscriptionManager.PORT_INDEX, insertPortIndex);
 
         Log.d(TAG, "testSimTable Inserting contentValues: " + contentValues);
         mContentResolver.insert(SimInfo.CONTENT_URI, contentValues);
@@ -718,6 +724,7 @@
             SubscriptionManager.CARRIER_NAME,
             SubscriptionManager.CARD_ID,
             SubscriptionManager.PROFILE_CLASS,
+            SubscriptionManager.PORT_INDEX,
         };
         final String selection = SubscriptionManager.DISPLAY_NAME + "=?";
         String[] selectionArgs = { insertDisplayName };
@@ -734,10 +741,11 @@
         final String resultCarrierName = cursor.getString(1);
         final String resultCardId = cursor.getString(2);
         final int resultProfileClass = cursor.getInt(3);
+        final int resultPortIndex = cursor.getInt(4);
         assertEquals(insertSubId, resultSubId);
         assertEquals(insertCarrierName, resultCarrierName);
         assertEquals(insertCardId, resultCardId);
-        assertEquals(insertProfileClass, resultProfileClass);
+        assertEquals(insertPortIndex, resultPortIndex);
 
         // delete test content
         final String selectionToDelete = SubscriptionManager.DISPLAY_NAME + "=?";