Merge "Snap for 9550355 from f59679a1c63358294bac32b66a5ade819e2d123d to sdk-release" into sdk-release
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7eb3721
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.iml
+*.idea
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/assets/latest_carrier_id/carrier_list.pb b/assets/latest_carrier_id/carrier_list.pb
index d0301ec..634a662 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 9241f68..684700d 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 ad152ce..601dbb5 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 3485f10..b5adc83 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 8d5466e..117d909 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 3ed9003..a712555 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
index 424d582..14a9e2a 100644
--- a/assets/sdk30_carrier_id/carrier_list.pb
+++ 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
index 861df90..c5fe0dd 100644
--- a/assets/sdk30_carrier_id/carrier_list.textpb
+++ b/assets/sdk30_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/assets/sdk31_carrier_id/carrier_list.pb b/assets/sdk31_carrier_id/carrier_list.pb
index 8af436d..94a70dd 100644
--- a/assets/sdk31_carrier_id/carrier_list.pb
+++ b/assets/sdk31_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/sdk31_carrier_id/carrier_list.textpb b/assets/sdk31_carrier_id/carrier_list.textpb
index 8cd07c4..dfea390 100644
--- a/assets/sdk31_carrier_id/carrier_list.textpb
+++ b/assets/sdk31_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 96059d7..bc41208 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -38,7 +38,6 @@
 import android.provider.Telephony.CanonicalAddressesColumns;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.Mms.Addr;
-import android.provider.Telephony.Mms.Inbox;
 import android.provider.Telephony.Mms.Part;
 import android.provider.Telephony.Mms.Rate;
 import android.provider.Telephony.MmsSms;
@@ -46,8 +45,11 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import com.google.android.mms.pdu.PduHeaders;
 import com.google.android.mms.util.DownloadDrmHelper;
 
@@ -71,6 +73,13 @@
     // The name of parts directory. The full dir is "app_parts".
     static final String PARTS_DIR_NAME = "parts";
 
+    private ProviderUtilWrapper providerUtilWrapper = new ProviderUtilWrapper();
+
+    @VisibleForTesting
+    public void setProviderUtilWrapper(ProviderUtilWrapper providerUtilWrapper) {
+        this.providerUtilWrapper = providerUtilWrapper;
+    }
+
     @Override
     public boolean onCreate() {
         setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
@@ -79,6 +88,14 @@
         return true;
     }
 
+    // wrapper class to allow easier mocking of the static ProviderUtil in tests
+    @VisibleForTesting
+    public static class ProviderUtilWrapper {
+        public boolean isAccessRestricted(Context context, String packageName, int uid) {
+            return ProviderUtil.isAccessRestricted(context, packageName, uid);
+        }
+    }
+
     /**
      * Return the proper view of "pdu" table for the current access status.
      *
@@ -98,8 +115,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.
@@ -310,6 +332,15 @@
         int msgBox = Mms.MESSAGE_BOX_ALL;
         boolean notify = true;
 
+        boolean forceNoNotify = values.containsKey(TelephonyBackupAgent.NOTIFY)
+                && !values.getAsBoolean(TelephonyBackupAgent.NOTIFY);
+        values.remove(TelephonyBackupAgent.NOTIFY);
+        // check isAccessRestricted to prevent third parties from setting NOTIFY = false maliciously
+        if (forceNoNotify && !providerUtilWrapper.isAccessRestricted(
+                getContext(), getCallingPackage(), Binder.getCallingUid())) {
+            notify = false;
+        }
+
         int match = sURLMatcher.match(uri);
         if (LOCAL_LOGV) {
             Log.v(TAG, "Insert uri=" + uri + ", match=" + match);
@@ -820,13 +851,21 @@
             case MMS_PART_RESET_FILE_PERMISSION:
                 String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath() + '/' +
                         uri.getPathSegments().get(1);
-                // Reset the file permission back to read for everyone but me.
+
                 try {
+                    String partsDirPath = getContext().getDir(PARTS_DIR_NAME, 0).getCanonicalPath();
+                    if (!new File(path).getCanonicalPath().startsWith(partsDirPath)) {
+                        EventLog.writeEvent(0x534e4554, "240685104",
+                                Binder.getCallingUid(), (TAG + " update: path " + path +
+                                        " does not start with " + partsDirPath));
+                        return 0;
+                    }
+                    // Reset the file permission back to read for everyone but me.
                     Os.chmod(path, 0644);
                     if (LOCAL_LOGV) {
                         Log.d(TAG, "MmsProvider.update chmod is successful for path: " + path);
                     }
-                } catch (ErrnoException e) {
+                } catch (ErrnoException | IOException e) {
                     Log.e(TAG, "Exception in chmod: " + e);
                 }
                 return 0;
@@ -1056,7 +1095,8 @@
         sURLMatcher.addURI("mms", "resetFilePerm/*",    MMS_PART_RESET_FILE_PERMISSION);
     }
 
-    private SQLiteOpenHelper mOpenHelper;
+    @VisibleForTesting
+    public SQLiteOpenHelper mOpenHelper;
 
     private static String concatSelections(String selection1, String selection2) {
         if (TextUtils.isEmpty(selection1)) {
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index a9494fb..22b846c 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -49,6 +49,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
 
 import com.google.android.mms.pdu.EncodedStringValue;
 import com.google.android.mms.pdu.PduHeaders;
@@ -81,6 +82,14 @@
  */
 public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
     private static final String TAG = "MmsSmsDatabaseHelper";
+    private static final int SECURITY_EXCEPTION = TelephonyStatsLog
+            .MMS_SMS_DATABASE_HELPER_ON_UPGRADE_FAILED__FAILURE_CODE__FAILURE_SECURITY_EXCEPTION;
+    private static final int FAILURE_UNKNOWN = TelephonyStatsLog
+        .MMS_SMS_DATABASE_HELPER_ON_UPGRADE_FAILED__FAILURE_CODE__FAILURE_UNKNOWN;
+    private static final int SQL_EXCEPTION = TelephonyStatsLog
+            .MMS_SMS_DATABASE_HELPER_ON_UPGRADE_FAILED__FAILURE_CODE__FAILURE_SQL_EXCEPTION;
+    private static final int IO_EXCEPTION = TelephonyStatsLog
+            .MMS_SMS_DATABASE_HELPER_ON_UPGRADE_FAILED__FAILURE_CODE__FAILURE_IO_EXCEPTION;
 
     private static final String SMS_UPDATE_THREAD_READ_BODY =
                         "  UPDATE threads SET read = " +
@@ -295,7 +304,7 @@
         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
         setWriteAheadLoggingEnabled(false);
         try {
-            PhoneFactory.addLocalLog(TAG, 100);
+            PhoneFactory.addLocalLog(TAG, 64);
         } catch (IllegalArgumentException e) {
             // ignore
         }
@@ -653,6 +662,11 @@
     }
 
     private void createWordsTables(SQLiteDatabase db) {
+        createWordsTables(db, -1, -1, -1);
+    }
+
+    private void createWordsTables(
+            SQLiteDatabase db, int oldVersion, int currentVersion, int upgradeVersion) {
         try {
             db.execSQL("CREATE VIRTUAL TABLE words USING FTS3 (_id INTEGER PRIMARY KEY, index_text TEXT, source_id INTEGER, table_to_use INTEGER);");
 
@@ -670,6 +684,7 @@
             populateWordsTable(db);
         } catch (Exception ex) {
             Log.e(TAG, "got exception creating words table: " + ex.toString());
+            logException(ex, oldVersion, currentVersion, upgradeVersion);
         }
     }
 
@@ -681,112 +696,156 @@
     }
 
     private void createThreadIdIndex(SQLiteDatabase db) {
+        createThreadIdIndex(db, -1, -1, -1);
+    }
+
+    private void createThreadIdIndex(
+            SQLiteDatabase db, int oldVersion, int currentVersion, int upgradeVersion) {
         try {
             db.execSQL("CREATE INDEX IF NOT EXISTS typeThreadIdIndex ON sms" +
             " (type, thread_id);");
         } catch (Exception ex) {
             Log.e(TAG, "got exception creating indices: " + ex.toString());
+            logException(ex, oldVersion, currentVersion, upgradeVersion);
         }
     }
 
     private void createThreadIdDateIndex(SQLiteDatabase db) {
+        createThreadIdDateIndex(db, -1, -1, -1);
+    }
+
+    private void createThreadIdDateIndex(
+            SQLiteDatabase db, int oldVersion, int currentVersion, int upgradeVersion) {
         try {
             db.execSQL("CREATE INDEX IF NOT EXISTS threadIdDateIndex ON sms" +
             " (thread_id, date);");
         } catch (Exception ex) {
             Log.e(TAG, "got exception creating indices: " + ex.toString());
+            logException(ex, oldVersion, currentVersion, upgradeVersion);
         }
     }
 
     private void createPartMidIndex(SQLiteDatabase db) {
+        createPartMidIndex(db, -1, -1, -1);
+    }
+
+    private void createPartMidIndex(
+            SQLiteDatabase db, int oldVersion, int currentVersion, int upgradeVersion) {
         try {
             db.execSQL("CREATE INDEX IF NOT EXISTS partMidIndex ON part (mid)");
         } catch (Exception ex) {
             Log.e(TAG, "got exception creating indices: " + ex.toString());
+            logException(ex, oldVersion, currentVersion, upgradeVersion);
         }
     }
 
     private void createAddrMsgIdIndex(SQLiteDatabase db) {
+        createAddrMsgIdIndex(db, -1, -1, -1);
+    }
+
+    private void createAddrMsgIdIndex(
+            SQLiteDatabase db, int oldVersion, int currentVersion, int upgradeVersion) {
         try {
             db.execSQL("CREATE INDEX IF NOT EXISTS addrMsgIdIndex ON addr (msg_id)");
         } catch (Exception ex) {
             Log.e(TAG, "got exception creating indices: " + ex.toString());
+            logException(ex, oldVersion, currentVersion, upgradeVersion);
         }
     }
 
+
+    @VisibleForTesting
+    public static String CREATE_ADDR_TABLE_STR =
+            "CREATE TABLE " + MmsProvider.TABLE_ADDR + " (" +
+            Addr._ID + " INTEGER PRIMARY KEY," +
+            Addr.MSG_ID + " INTEGER," +
+            Addr.CONTACT_ID + " INTEGER," +
+            Addr.ADDRESS + " TEXT," +
+            Addr.TYPE + " INTEGER," +
+            Addr.CHARSET + " INTEGER);";
+
+    @VisibleForTesting
+    public static String CREATE_PART_TABLE_STR =
+            "CREATE TABLE " + MmsProvider.TABLE_PART + " (" +
+            Part._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+            Part.MSG_ID + " INTEGER," +
+            Part.SEQ + " INTEGER DEFAULT 0," +
+            Part.CONTENT_TYPE + " TEXT," +
+            Part.NAME + " TEXT," +
+            Part.CHARSET + " INTEGER," +
+            Part.CONTENT_DISPOSITION + " TEXT," +
+            Part.FILENAME + " TEXT," +
+            Part.CONTENT_ID + " TEXT," +
+            Part.CONTENT_LOCATION + " TEXT," +
+            Part.CT_START + " INTEGER," +
+            Part.CT_TYPE + " TEXT," +
+            Part._DATA + " TEXT," +
+            Part.TEXT + " TEXT);";
+
+    public static String CREATE_PDU_TABLE_STR =
+            "CREATE TABLE " + MmsProvider.TABLE_PDU + " (" +
+            Mms._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+            Mms.THREAD_ID + " INTEGER," +
+            Mms.DATE + " INTEGER," +
+            Mms.DATE_SENT + " INTEGER DEFAULT 0," +
+            Mms.MESSAGE_BOX + " INTEGER," +
+            Mms.READ + " INTEGER DEFAULT 0," +
+            Mms.MESSAGE_ID + " TEXT," +
+            Mms.SUBJECT + " TEXT," +
+            Mms.SUBJECT_CHARSET + " INTEGER," +
+            Mms.CONTENT_TYPE + " TEXT," +
+            Mms.CONTENT_LOCATION + " TEXT," +
+            Mms.EXPIRY + " INTEGER," +
+            Mms.MESSAGE_CLASS + " TEXT," +
+            Mms.MESSAGE_TYPE + " INTEGER," +
+            Mms.MMS_VERSION + " INTEGER," +
+            Mms.MESSAGE_SIZE + " INTEGER," +
+            Mms.PRIORITY + " INTEGER," +
+            Mms.READ_REPORT + " INTEGER," +
+            Mms.REPORT_ALLOWED + " INTEGER," +
+            Mms.RESPONSE_STATUS + " INTEGER," +
+            Mms.STATUS + " INTEGER," +
+            Mms.TRANSACTION_ID + " TEXT," +
+            Mms.RETRIEVE_STATUS + " INTEGER," +
+            Mms.RETRIEVE_TEXT + " TEXT," +
+            Mms.RETRIEVE_TEXT_CHARSET + " INTEGER," +
+            Mms.READ_STATUS + " INTEGER," +
+            Mms.CONTENT_CLASS + " INTEGER," +
+            Mms.RESPONSE_TEXT + " TEXT," +
+            Mms.DELIVERY_TIME + " INTEGER," +
+            Mms.DELIVERY_REPORT + " INTEGER," +
+            Mms.LOCKED + " INTEGER DEFAULT 0," +
+            Mms.SUBSCRIPTION_ID + " INTEGER DEFAULT "
+                    + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
+            Mms.SEEN + " INTEGER DEFAULT 0," +
+            Mms.CREATOR + " TEXT," +
+            Mms.TEXT_ONLY + " INTEGER DEFAULT 0);";
+
+    @VisibleForTesting
+    public static String CREATE_RATE_TABLE_STR =
+            "CREATE TABLE " + MmsProvider.TABLE_RATE + " (" +
+            Rate.SENT_TIME + " INTEGER);";
+
+    @VisibleForTesting
+    public static String CREATE_DRM_TABLE_STR =
+            "CREATE TABLE " + MmsProvider.TABLE_DRM + " (" +
+            BaseColumns._ID + " INTEGER PRIMARY KEY," +
+            "_data TEXT);";
+
     @VisibleForTesting
     void createMmsTables(SQLiteDatabase db) {
         // N.B.: Whenever the columns here are changed, the columns in
         // {@ref MmsSmsProvider} must be changed to match.
-        db.execSQL("CREATE TABLE " + MmsProvider.TABLE_PDU + " (" +
-                   Mms._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
-                   Mms.THREAD_ID + " INTEGER," +
-                   Mms.DATE + " INTEGER," +
-                   Mms.DATE_SENT + " INTEGER DEFAULT 0," +
-                   Mms.MESSAGE_BOX + " INTEGER," +
-                   Mms.READ + " INTEGER DEFAULT 0," +
-                   Mms.MESSAGE_ID + " TEXT," +
-                   Mms.SUBJECT + " TEXT," +
-                   Mms.SUBJECT_CHARSET + " INTEGER," +
-                   Mms.CONTENT_TYPE + " TEXT," +
-                   Mms.CONTENT_LOCATION + " TEXT," +
-                   Mms.EXPIRY + " INTEGER," +
-                   Mms.MESSAGE_CLASS + " TEXT," +
-                   Mms.MESSAGE_TYPE + " INTEGER," +
-                   Mms.MMS_VERSION + " INTEGER," +
-                   Mms.MESSAGE_SIZE + " INTEGER," +
-                   Mms.PRIORITY + " INTEGER," +
-                   Mms.READ_REPORT + " INTEGER," +
-                   Mms.REPORT_ALLOWED + " INTEGER," +
-                   Mms.RESPONSE_STATUS + " INTEGER," +
-                   Mms.STATUS + " INTEGER," +
-                   Mms.TRANSACTION_ID + " TEXT," +
-                   Mms.RETRIEVE_STATUS + " INTEGER," +
-                   Mms.RETRIEVE_TEXT + " TEXT," +
-                   Mms.RETRIEVE_TEXT_CHARSET + " INTEGER," +
-                   Mms.READ_STATUS + " INTEGER," +
-                   Mms.CONTENT_CLASS + " INTEGER," +
-                   Mms.RESPONSE_TEXT + " TEXT," +
-                   Mms.DELIVERY_TIME + " INTEGER," +
-                   Mms.DELIVERY_REPORT + " INTEGER," +
-                   Mms.LOCKED + " INTEGER DEFAULT 0," +
-                   Mms.SUBSCRIPTION_ID + " INTEGER DEFAULT "
-                           + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
-                   Mms.SEEN + " INTEGER DEFAULT 0," +
-                   Mms.CREATOR + " TEXT," +
-                   Mms.TEXT_ONLY + " INTEGER DEFAULT 0" +
-                   ");");
 
-        db.execSQL("CREATE TABLE " + MmsProvider.TABLE_ADDR + " (" +
-                   Addr._ID + " INTEGER PRIMARY KEY," +
-                   Addr.MSG_ID + " INTEGER," +
-                   Addr.CONTACT_ID + " INTEGER," +
-                   Addr.ADDRESS + " TEXT," +
-                   Addr.TYPE + " INTEGER," +
-                   Addr.CHARSET + " INTEGER);");
+        db.execSQL(CREATE_PDU_TABLE_STR);
 
-        db.execSQL("CREATE TABLE " + MmsProvider.TABLE_PART + " (" +
-                   Part._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
-                   Part.MSG_ID + " INTEGER," +
-                   Part.SEQ + " INTEGER DEFAULT 0," +
-                   Part.CONTENT_TYPE + " TEXT," +
-                   Part.NAME + " TEXT," +
-                   Part.CHARSET + " INTEGER," +
-                   Part.CONTENT_DISPOSITION + " TEXT," +
-                   Part.FILENAME + " TEXT," +
-                   Part.CONTENT_ID + " TEXT," +
-                   Part.CONTENT_LOCATION + " TEXT," +
-                   Part.CT_START + " INTEGER," +
-                   Part.CT_TYPE + " TEXT," +
-                   Part._DATA + " TEXT," +
-                   Part.TEXT + " TEXT);");
+        db.execSQL(CREATE_ADDR_TABLE_STR);
 
-        db.execSQL("CREATE TABLE " + MmsProvider.TABLE_RATE + " (" +
-                   Rate.SENT_TIME + " INTEGER);");
+        db.execSQL(CREATE_PART_TABLE_STR);
 
-        db.execSQL("CREATE TABLE " + MmsProvider.TABLE_DRM + " (" +
-                   BaseColumns._ID + " INTEGER PRIMARY KEY," +
-                   "_data TEXT);");
+        db.execSQL(CREATE_RATE_TABLE_STR);
+
+        db.execSQL(CREATE_DRM_TABLE_STR);
 
         // Restricted view of pdu table, only sent/received messages without wap pushes
         db.execSQL("CREATE VIEW " + MmsProvider.VIEW_PDU_RESTRICTED + " AS " +
@@ -1248,6 +1307,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 41);
                 break;
             } finally {
                 db.endTransaction();
@@ -1264,6 +1324,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 42);
                 break;
             } finally {
                 db.endTransaction();
@@ -1280,6 +1341,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 43);
                 break;
             } finally {
                 db.endTransaction();
@@ -1296,6 +1358,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 44);
                 break;
             } finally {
                 db.endTransaction();
@@ -1312,6 +1375,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 45);
                 break;
             } finally {
                 db.endTransaction();
@@ -1323,10 +1387,11 @@
             }
             db.beginTransaction();
             try {
-                upgradeDatabaseToVersion46(db);
+                upgradeDatabaseToVersion46(db, oldVersion, currentVersion);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 46);
                 break;
             } finally {
                 db.endTransaction();
@@ -1343,6 +1408,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 47);
                 break;
             } finally {
                 db.endTransaction();
@@ -1359,6 +1425,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 48);
                 break;
             } finally {
                 db.endTransaction();
@@ -1371,10 +1438,11 @@
 
             db.beginTransaction();
             try {
-                createWordsTables(db);
+                createWordsTables(db, oldVersion, currentVersion, 49);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 49);
                 break;
             } finally {
                 db.endTransaction();
@@ -1386,10 +1454,11 @@
             }
             db.beginTransaction();
             try {
-                createThreadIdIndex(db);
+                createThreadIdIndex(db, oldVersion, currentVersion, 50);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 50);
                 break; // force to destroy all old data;
             } finally {
                 db.endTransaction();
@@ -1406,6 +1475,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 51);
                 break;
             } finally {
                 db.endTransaction();
@@ -1428,6 +1498,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 53);
                 break;
             } finally {
                 db.endTransaction();
@@ -1444,6 +1515,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 54);
                 break;
             } finally {
                 db.endTransaction();
@@ -1460,6 +1532,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 55);
                 break;
             } finally {
                 db.endTransaction();
@@ -1476,6 +1549,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 56);
                 break;
             } finally {
                 db.endTransaction();
@@ -1492,6 +1566,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 57);
                 break;
             } finally {
                 db.endTransaction();
@@ -1508,6 +1583,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 58);
                 break;
             } finally {
                 db.endTransaction();
@@ -1524,6 +1600,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 59);
                 break;
             } finally {
                 db.endTransaction();
@@ -1540,6 +1617,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 60);
                 break;
             } finally {
                 db.endTransaction();
@@ -1556,6 +1634,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 61);
                 break;
             } finally {
                 db.endTransaction();
@@ -1568,10 +1647,11 @@
 
             db.beginTransaction();
             try {
-                upgradeDatabaseToVersion62(db);
+                upgradeDatabaseToVersion62(db, oldVersion, currentVersion);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 62);
                 break;
             } finally {
                 db.endTransaction();
@@ -1585,10 +1665,11 @@
             db.beginTransaction();
             try {
                 // upgrade to 63: just add a happy little index.
-                createThreadIdDateIndex(db);
+                createThreadIdDateIndex(db, oldVersion, currentVersion, 63);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 63);
                 break;
             } finally {
                 db.endTransaction();
@@ -1605,6 +1686,7 @@
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 64);
                 break;
             } finally {
                 db.endTransaction();
@@ -1617,10 +1699,11 @@
 
             db.beginTransaction();
             try {
-                upgradeDatabaseToVersion65(db);
+                upgradeDatabaseToVersion65(db, oldVersion, currentVersion);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 65);
                 break;
             } finally {
                 db.endTransaction();
@@ -1633,10 +1716,11 @@
 
             db.beginTransaction();
             try {
-                upgradeDatabaseToVersion66(db);
+                upgradeDatabaseToVersion66(db, oldVersion, currentVersion);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 66);
                 break;
             } finally {
                 db.endTransaction();
@@ -1648,16 +1732,17 @@
             }
             db.beginTransaction();
             try {
-                createPartMidIndex(db);
-                createAddrMsgIdIndex(db);
+                createPartMidIndex(db, oldVersion, currentVersion, 67);
+                createAddrMsgIdIndex(db, oldVersion, currentVersion, 67);
                 db.setTransactionSuccessful();
             } catch (Throwable ex) {
                 Log.e(TAG, ex.getMessage(), ex);
+                logException(ex, oldVersion, currentVersion, 67);
                 break; // force to destroy all old data;
             } finally {
                 db.endTransaction();
             }
-            // fall through
+            return;
         }
 
         Log.e(TAG, "Destroying all old data.");
@@ -1667,6 +1752,24 @@
         onCreate(db);
     }
 
+    private void logException(
+            Throwable ex, int oldVersion, int currentVersion, int upgradeVersion) {
+        int exception = FAILURE_UNKNOWN;
+        if (ex instanceof SQLiteException) {
+            exception = SQL_EXCEPTION;
+        } else if (ex instanceof IOException) {
+            exception = IO_EXCEPTION;
+        } else if (ex instanceof SecurityException) {
+            exception = SECURITY_EXCEPTION;
+        }
+        TelephonyStatsLog.write(
+            TelephonyStatsLog.MMS_SMS_DATABASE_HELPER_ON_UPGRADE_FAILED,
+            oldVersion,
+            currentVersion,
+            upgradeVersion,
+            exception);
+    }
+
     private void dropAll(SQLiteDatabase db) {
         // Clean the database out in order to start over from scratch.
         // We don't need to drop our triggers here because SQLite automatically
@@ -1733,7 +1836,7 @@
         db.execSQL("ALTER TABLE pdu ADD COLUMN " + Mms.LOCKED + " INTEGER DEFAULT 0");
     }
 
-    private void upgradeDatabaseToVersion46(SQLiteDatabase db) {
+    private void upgradeDatabaseToVersion46(SQLiteDatabase db, int oldVersion, int currentVersion) {
         // add the "text" column for caching inline text (e.g. strings) instead of
         // putting them in an external file
         db.execSQL("ALTER TABLE part ADD COLUMN " + Part.TEXT + " TEXT");
@@ -1771,6 +1874,7 @@
                         } catch (IOException e) {
                             // TODO Auto-generated catch block
                             e.printStackTrace();
+                            logException(e, oldVersion, currentVersion, 46);
                         }
                     }
                 }
@@ -1783,6 +1887,7 @@
                     (new File(pathToDelete)).delete();
                 } catch (SecurityException ex) {
                     Log.e(TAG, "unable to clean up old mms file for " + pathToDelete, ex);
+                    logException(ex, oldVersion, currentVersion, 46);
                 }
             }
             if (textRows != null) {
@@ -1904,7 +2009,7 @@
 
     }
 
-    private void upgradeDatabaseToVersion62(SQLiteDatabase db) {
+    private void upgradeDatabaseToVersion62(SQLiteDatabase db, int oldVersion, int currentVersion) {
         // When a non-FBE device is upgraded to N, all MMS attachment files are moved from
         // /data/data to /data/user_de. We need to update the paths stored in the parts table to
         // reflect this change.
@@ -1914,6 +2019,7 @@
         }
         catch (IOException e){
             Log.e(TAG, "openFile: check file path failed " + e, e);
+            logException(e, oldVersion, currentVersion, 62);
             return;
         }
 
@@ -1938,7 +2044,7 @@
         db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW +" ADD COLUMN deleted INTEGER DEFAULT 0");
     }
 
-    private void upgradeDatabaseToVersion65(SQLiteDatabase db) {
+    private void upgradeDatabaseToVersion65(SQLiteDatabase db, int oldVersion, int currentVersion) {
         // aosp and internal code diverged at version 63. Aosp did createThreadIdDateIndex() on
         // upgrading to 63, whereas internal (nyc) added column 'deleted'. A device upgrading from
         // nyc will have columns deleted and message_body in raw table with version 64, but not
@@ -1948,17 +2054,19 @@
         } catch (SQLiteException e) {
             Log.w(TAG, "[upgradeDatabaseToVersion65] Exception adding column message_body; " +
                     "trying createThreadIdDateIndex() instead: " + e);
+            logException(e, oldVersion, currentVersion, 65);
             createThreadIdDateIndex(db);
         }
     }
 
-    private void upgradeDatabaseToVersion66(SQLiteDatabase db) {
+    private void upgradeDatabaseToVersion66(SQLiteDatabase db, int oldVersion, int currentVersion) {
         try {
             db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW
                     + " ADD COLUMN display_originating_addr TEXT");
         } catch (SQLiteException e) {
             Log.e(TAG, "[upgradeDatabaseToVersion66] Exception adding column "
                     + "display_originating_addr; " + e);
+            logException(e, oldVersion, currentVersion, 66);
         }
     }
 
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 8e01bcc..904e2ba 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -43,6 +43,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.telephony.TelephonyStatsLog;
+
 import com.google.android.mms.pdu.PduHeaders;
 
 import java.io.FileDescriptor;
@@ -78,6 +80,10 @@
             new UriMatcher(UriMatcher.NO_MATCH);
     private static final String LOG_TAG = "MmsSmsProvider";
     private static final boolean DEBUG = false;
+    private static final int MULTIPLE_THREAD_IDS_FOUND = TelephonyStatsLog
+        .MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED__FAILURE_CODE__FAILURE_MULTIPLE_THREAD_IDS_FOUND;
+    private static final int FAILURE_FIND_OR_CREATE_THREAD_ID_SQL = TelephonyStatsLog
+        .MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED__FAILURE_CODE__FAILURE_FIND_OR_CREATE_THREAD_ID_SQL;
 
     private static final String NO_DELETES_INSERTS_OR_UPDATES =
             "MmsSmsProvider does not support deletes, inserts, or updates for this URI.";
@@ -355,8 +361,15 @@
                 if ((simple != null) && simple.equals("true")) {
                     String threadType = uri.getQueryParameter("thread_type");
                     if (!TextUtils.isEmpty(threadType)) {
-                        selection = concatSelections(
-                                selection, Threads.TYPE + "=" + threadType);
+                        try {
+                            Integer.parseInt(threadType);
+                            selection = concatSelections(
+                                    selection, Threads.TYPE + "=" + threadType);
+                        } catch (NumberFormatException ex) {
+                            Log.e(LOG_TAG, "Thread type must be int");
+                            // return empty cursor
+                            break;
+                        }
                     }
                     cursor = getSimpleConversations(
                             projection, selection, selectionArgs, sortOrder);
@@ -485,9 +498,15 @@
                 String extraSelection = (proto != -1) ?
                         (PendingMessages.PROTO_TYPE + "=" + proto) : " 0=0 ";
                 if (!TextUtils.isEmpty(msgId)) {
-                    extraSelection += " AND " + PendingMessages.MSG_ID + "=" + msgId;
+                    try {
+                        Long.parseLong(msgId);
+                        extraSelection += " AND " + PendingMessages.MSG_ID + "=" + msgId;
+                    } catch(NumberFormatException ex) {
+                        Log.e(LOG_TAG, "MSG ID must be a Long.");
+                        // return empty cursor
+                        break;
+                    }
                 }
-
                 String finalSelection = TextUtils.isEmpty(selection)
                         ? extraSelection : ("(" + extraSelection + ") AND " + selection);
                 String finalOrder = TextUtils.isEmpty(sortOrder)
@@ -686,6 +705,10 @@
         if (addressIds.size() == 0) {
             Log.e(LOG_TAG, "getThreadId: NO receipients specified -- NOT creating thread",
                     new Exception());
+            TelephonyStatsLog.write(
+                TelephonyStatsLog.MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED,
+                TelephonyStatsLog
+                    .MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED__FAILURE_CODE__FAILURE_NO_RECIPIENTS);
             return null;
         } else if (addressIds.size() == 1) {
             // optimize for size==1, which should be most of the cases
@@ -724,12 +747,18 @@
             db.setTransactionSuccessful();
         } catch (Throwable ex) {
             Log.e(LOG_TAG, ex.getMessage(), ex);
+            TelephonyStatsLog.write(
+                TelephonyStatsLog.MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED,
+                FAILURE_FIND_OR_CREATE_THREAD_ID_SQL);
         } finally {
             db.endTransaction();
         }
 
         if (cursor != null && cursor.getCount() > 1) {
             Log.w(LOG_TAG, "getThreadId: why is cursorCount=" + cursor.getCount());
+            TelephonyStatsLog.write(
+                TelephonyStatsLog.MMS_SMS_PROVIDER_GET_THREAD_ID_FAILED,
+                MULTIPLE_THREAD_IDS_FOUND);
         }
         return cursor;
     }
@@ -1022,7 +1051,6 @@
     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);
@@ -1033,8 +1061,7 @@
         String finalSmsSelection =
                 concatSelections(
                         selection,
-                        "(address=" + escapedPhoneNumber + " OR PHONE_NUMBERS_EQUAL(address, " +
-                        escapedPhoneNumber +
+                        "(address=? OR PHONE_NUMBERS_EQUAL(address, ?" +
                         (mUseStrictPhoneNumberComparation ? ", 1))" : ", 0, " + minMatch + "))"));
         SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
         SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
@@ -1044,9 +1071,8 @@
         mmsQueryBuilder.setTables(
                 pduTable +
                 ", (SELECT msg_id AS address_msg_id " +
-                "FROM addr WHERE (address=" + escapedPhoneNumber +
-                " OR PHONE_NUMBERS_EQUAL(addr.address, " +
-                escapedPhoneNumber +
+                "FROM addr WHERE (address=?" +
+                " OR PHONE_NUMBERS_EQUAL(addr.address, ?" +
                 (mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0, " + minMatch + "))) ") +
                 "AS matching_addresses");
         smsQueryBuilder.setTables(smsTable);
@@ -1065,7 +1091,8 @@
         String unionQuery = unionQueryBuilder.buildUnionQuery(
                 new String[] { mmsSubQuery, smsSubQuery }, sortOrder, null);
 
-        return mOpenHelper.getReadableDatabase().rawQuery(unionQuery, EMPTY_STRING_ARRAY);
+        return mOpenHelper.getReadableDatabase().rawQuery(unionQuery,
+                new String[] { phoneNumber, phoneNumber, phoneNumber, phoneNumber });
     }
 
     /**
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index 34fed99..5f75117 100644
--- a/src/com/android/providers/telephony/TelephonyBackupAgent.java
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -31,11 +31,11 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
 import android.provider.Telephony;
@@ -288,6 +288,8 @@
     private static final String QUOTA_RESET_TIME = "reset_quota_time";
     private static final long QUOTA_RESET_INTERVAL = 30 * AlarmManager.INTERVAL_DAY; // 30 days.
 
+    // Key for explicitly settings whether mms restore should notify or not
+    static final String NOTIFY = "notify";
 
     static {
         // Consider restored messages read and seen by default. The actual data can override
@@ -732,6 +734,7 @@
         jsonReader.beginArray();
         int total = 0;
         int numExceptions = 0;
+        final int notifyAfterCount = mMaxMsgPerFile;
         while (jsonReader.hasNext()) {
             final Mms mms = readMmsFromReader(jsonReader);
             if (DEBUG) {
@@ -747,17 +750,32 @@
                     continue;
                 }
                 total++;
+                mms.values.put(NOTIFY, false);
                 addMmsMessage(mms);
+                // notifying every 1000 messages to follow sms restore pattern
+                if (total % notifyAfterCount == 0) {
+                    notifyBulkMmsChange();
+                }
             } catch (Exception e) {
                 Log.e(TAG, "putMmsMessagesToProvider", e);
                 numExceptions++;
                 DeferredSmsMmsRestoreService.localLog("putMmsMessagesToProvider: Exception " + e);
             }
         }
+        // notifying for any remaining messages
+        if (total % notifyAfterCount > 0) {
+            notifyBulkMmsChange();
+        }
         Log.d(TAG, "putMmsMessagesToProvider handled " + total + " new messages.");
         incremenentSharedPref(false, total, numExceptions);
     }
 
+    private void notifyBulkMmsChange() {
+        mContentResolver.notifyChange(Telephony.MmsSms.CONTENT_URI, null,
+                ContentResolver.NOTIFY_SYNC_TO_NETWORK, UserHandle.USER_ALL);
+        ProviderUtil.notifyIfNotDefaultSmsApp(Telephony.Mms.CONTENT_URI, null, this);
+    }
+
     @VisibleForTesting
     static final String[] PROJECTION_ID = {BaseColumns._ID};
     private static final int ID_IDX = 0;
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 585873b..18aa8b9 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -108,6 +108,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Telephony;
+import android.service.carrier.IApnSourceService;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyProtoEnums;
@@ -116,41 +117,42 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Xml;
 
 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;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileDescriptor;
 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.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
-import java.util.zip.CheckedInputStream;
 import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
 
 public class TelephonyProvider extends ContentProvider
 {
@@ -159,7 +161,7 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private static final int DATABASE_VERSION = 56 << 16;
+    private static final int DATABASE_VERSION = 59 << 16;
     private static final int URL_UNKNOWN = 0;
     private static final int URL_TELEPHONY = 1;
     private static final int URL_CURRENT = 2;
@@ -281,6 +283,8 @@
 
     private boolean mManagedApnEnforced;
 
+    private final LocalLog mLocalLog = new LocalLog(128);
+
     /**
      * 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.
@@ -430,16 +434,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
@@ -566,13 +573,18 @@
                 + 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_PORT_INDEX + "  INTEGER DEFAULT -1"
+                + Telephony.SimInfo.COLUMN_PORT_INDEX + "  INTEGER DEFAULT -1,"
+                + Telephony.SimInfo.COLUMN_USAGE_SETTING + " INTEGER DEFAULT "
+                + SubscriptionManager.USAGE_SETTING_UNKNOWN + ","
+                + Telephony.SimInfo.COLUMN_TP_MESSAGE_REF +
+                "  INTEGER DEFAULT -1,"
+                + Telephony.SimInfo.COLUMN_USER_HANDLE + " INTEGER DEFAULT -1"
                 + ");";
     }
 
@@ -1656,21 +1668,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) {
@@ -1679,6 +1676,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;
             }
 
@@ -1700,7 +1711,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) {
@@ -1785,6 +1796,51 @@
                 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 (oldVersion < (58 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table with new columns.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_TP_MESSAGE_REF
+                            + "  INTEGER DEFAULT -1;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade failed to update " + SIMINFO_TABLE
+                                + " to add message Reference. ");
+                    }
+                }
+                oldVersion = 58 << 16 | 6;
+            }
+
+            if (oldVersion < (59 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table with new columns.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_USER_HANDLE
+                            + "  INTEGER DEFAULT -1;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade failed to update " + SIMINFO_TABLE
+                                + " to add message Reference. ");
+                    }
+                }
+                oldVersion = 59 << 16 | 6;
+            }
             if (DBG) {
                 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
             }
@@ -3005,13 +3061,6 @@
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext());
-
-        try {
-            PhoneFactory.addLocalLog(TAG, 100);
-        } catch (IllegalArgumentException e) {
-            // ignore
-        }
-
         boolean isNewBuild = false;
         String newBuildId = SystemProperties.get("ro.build.id", null);
         SharedPreferences sp = getContext().getSharedPreferences(BUILD_ID_FILE,
@@ -3081,9 +3130,9 @@
         }
     }
 
-    private static void localLog(String logMsg) {
+    private void localLog(String logMsg) {
         Log.d(TAG, logMsg);
-        PhoneFactory.localLog(TAG, logMsg);
+        mLocalLog.log(logMsg);
     }
 
     private synchronized boolean isManagedApnEnforced() {
@@ -3244,8 +3293,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) {
@@ -3616,7 +3667,7 @@
                 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion,
                 String isoCountryCodeFromDb,
                 List<String> wfcRestoreBlockedCountries) {
-            if (DATABASE_VERSION != 56 << 16) {
+            if (DATABASE_VERSION != 59 << 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 "
@@ -3658,6 +3709,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(
@@ -3794,8 +3851,9 @@
     @Override
     public synchronized Cursor query(Uri url, String[] projectionIn, String selection,
             String[] selectionArgs, String sort) {
-        if (VDBG) log("query: url=" + url + ", projectionIn=" + projectionIn + ", selection="
-                + selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);
+        if (VDBG) log("query: url=" + url + ", projectionIn=" + Arrays.toString(projectionIn)
+                + ", selection=" + selection + "selectionArgs=" + Arrays.toString(selectionArgs)
+                + ", sort=" + sort);
         int subId = SubscriptionManager.getDefaultSubscriptionId();
         String subIdString;
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -3907,6 +3965,14 @@
                 break;
             }
 
+            case URL_DPC_ID: {
+                constraints.add("_id = " + url.getLastPathSegment());
+                ensureCallingFromSystemOrPhoneUid("URL_DPC called from non SYSTEM_UID.");
+                // DPC query only returns DPC records.
+                constraints.add(IS_OWNED_BY_DPC);
+                break;
+            }
+
             case URL_FILTERED_ID:
             case URL_FILTERED_USING_SUBID: {
                 String idString = url.getLastPathSegment();
@@ -4300,12 +4366,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:
@@ -4453,10 +4519,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:
@@ -4615,11 +4681,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:
@@ -4893,6 +4959,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(
@@ -4907,7 +4979,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) {
@@ -4948,12 +5041,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) ||
@@ -4965,17 +5060,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) {
@@ -5360,4 +5465,72 @@
                     new String[]{subId});
         }
     }
+
+    /**
+     * Dump the database table.
+     *
+     * @param tableName Table name.
+     * @param pw Print writer.
+     */
+    private void dumpTable(@NonNull String tableName, @NonNull IndentingPrintWriter pw) {
+        try (Cursor cursor = getReadableDatabase().query(false, tableName, null,
+                null, null, null, null, null, null)) {
+            pw.println(tableName + " table:");
+            pw.increaseIndent();
+            while (cursor != null && cursor.moveToNext()) {
+                List<String> columnStrings = new ArrayList<>();
+                String str = "";
+                for (int i = 0; i < cursor.getColumnCount(); i++) {
+                    str = cursor.getColumnName(i) + "=";
+                    int type = cursor.getType(i);
+                    try {
+                        switch (type) {
+                            case 0 /*FIELD_TYPE_NULL*/:
+                                str += "null";
+                                break;
+                            case 1 /*FIELD_TYPE_INTEGER*/:
+                                str += cursor.getInt(i);
+                                break;
+                            case 2 /*FIELD_TYPE_FLOAT*/:
+                                str += cursor.getFloat(i);
+                                break;
+                            case 3 /*FIELD_TYPE_STRING*/:
+                                str += cursor.getString(i);
+                                break;
+                            case 4 /*FIELD_TYPE_BLOB*/:
+                                str += "[blob]";
+                                break;
+                            default:
+                                str += "unknown";
+                                break;
+                        }
+                    } catch (Exception e) {
+                        str += "exception";
+                    }
+                    columnStrings.add(str);
+                }
+                pw.println(TextUtils.join(", ", columnStrings));
+            }
+            pw.decreaseIndent();
+        } catch (Exception ex) {
+            pw.println("Exception while dumping the table " + tableName + ", ex=" + ex);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println(TAG + ":");
+        pw.increaseIndent();
+        pw.println("Database:");
+        pw.increaseIndent();
+        dumpTable(SIMINFO_TABLE, pw);
+        dumpTable(CARRIERS_TABLE, pw);
+        pw.decreaseIndent();
+        pw.println("Local log:");
+        pw.increaseIndent();
+        mLocalLog.dump(pw);
+        pw.decreaseIndent();
+        pw.decreaseIndent();
+    }
 }
diff --git a/tests/src/com/android/providers/telephony/MmsProviderTest.java b/tests/src/com/android/providers/telephony/MmsProviderTest.java
new file mode 100644
index 0000000..2e618b0
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/MmsProviderTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.telephony;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class MmsProviderTest extends TestCase {
+    private static final String TAG = "MmsProviderTest";
+
+    private MockContentResolver mContentResolver;
+    private MmsProviderTestable mMmsProviderTestable;
+
+    private int notifyChangeCount;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mMmsProviderTestable = new MmsProviderTestable();
+
+        // setup mocks
+        Context context = mock(Context.class);
+        PackageManager packageManager = mock(PackageManager.class);
+        Resources resources = mock(Resources.class);
+        when(context.getSystemService(eq(Context.APP_OPS_SERVICE)))
+                .thenReturn(mock(AppOpsManager.class));
+        when(context.getSystemService(eq(Context.TELEPHONY_SERVICE)))
+                .thenReturn(mock(TelephonyManager.class));
+
+        when(context.checkCallingOrSelfPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(context.getUserId()).thenReturn(0);
+        when(context.getPackageManager()).thenReturn(packageManager);
+        when(context.getResources()).thenReturn(resources);
+        when(resources.getString(anyInt())).thenReturn("");
+
+        /**
+         * This is used to give the MmsProviderTest a mocked context which takes a
+         * MmsProvider and attaches it to the ContentResolver with telephony authority.
+         * The mocked context also gives WRITE_APN_SETTINGS permissions
+         */
+        mContentResolver = new MockContentResolver() {
+            @Override
+            public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
+                    int userHandle) {
+                notifyChangeCount++;
+            }
+        };
+        when(context.getContentResolver()).thenReturn(mContentResolver);
+
+        // Add authority="mms" to given mmsProvider
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = "mms";
+
+        // Add context to given mmsProvider
+        mMmsProviderTestable.attachInfoForTesting(context, providerInfo);
+        Log.d(TAG, "MockContextWithProvider: mmsProvider.getContext(): "
+                + mMmsProviderTestable.getContext());
+
+        // Add given MmsProvider to mResolver with authority="mms" so that
+        // mResolver can send queries to mMmsProvider
+        mContentResolver.addProvider("mms", mMmsProviderTestable);
+        Log.d(TAG, "MockContextWithProvider: Add MmsProvider to mResolver");
+        notifyChangeCount = 0;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mMmsProviderTestable.closeDatabase();
+    }
+
+    @Test
+    public void testInsertMms() {
+        final ContentValues values = getTestContentValues();
+
+        Uri expected = Uri.parse("content://mms/1");
+        Uri actual = mContentResolver.insert(Telephony.Mms.CONTENT_URI, values);
+
+        assertEquals(expected, actual);
+        assertEquals(1, notifyChangeCount);
+    }
+
+    @Test
+    public void testInsertMmsWithoutNotify() {
+
+        MmsProvider.ProviderUtilWrapper providerUtilWrapper =
+                mock(MmsProvider.ProviderUtilWrapper.class);
+        when(providerUtilWrapper.isAccessRestricted(
+                any(Context.class), anyString(), anyInt())).thenReturn(false);
+        mMmsProviderTestable.setProviderUtilWrapper(providerUtilWrapper);
+
+        final ContentValues values = getTestContentValues();
+        values.put(TelephonyBackupAgent.NOTIFY, false);
+
+        Uri expected = Uri.parse("content://mms/1");
+        Uri actual = mContentResolver.insert(Telephony.Mms.CONTENT_URI, values);
+
+        assertEquals(expected, actual);
+        assertEquals(0, notifyChangeCount);
+    }
+
+    private ContentValues getTestContentValues() {
+        final ContentValues values = new ContentValues();
+        values.put(Telephony.Mms.READ, 1);
+        values.put(Telephony.Mms.SEEN, 1);
+        values.put(Telephony.Mms.SUBSCRIPTION_ID, 1);
+        values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_ALL);
+        values.put(Telephony.Mms.TEXT_ONLY, 1);
+        values.put(Telephony.Mms.THREAD_ID, 1);
+        return values;
+    }
+}
diff --git a/tests/src/com/android/providers/telephony/MmsProviderTestable.java b/tests/src/com/android/providers/telephony/MmsProviderTestable.java
new file mode 100644
index 0000000..cfc0359
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/MmsProviderTestable.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.providers.telephony;
+
+import static com.android.providers.telephony.MmsSmsDatabaseHelper.CREATE_ADDR_TABLE_STR;
+import static com.android.providers.telephony.MmsSmsDatabaseHelper.CREATE_DRM_TABLE_STR;
+import static com.android.providers.telephony.MmsSmsDatabaseHelper.CREATE_PART_TABLE_STR;
+import static com.android.providers.telephony.MmsSmsDatabaseHelper.CREATE_PDU_TABLE_STR;
+import static com.android.providers.telephony.MmsSmsDatabaseHelper.CREATE_RATE_TABLE_STR;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * A subclass of MmsProvider used for testing on an in-memory database
+ */
+public class MmsProviderTestable extends MmsProvider {
+    private static final String TAG = "MmsProviderTestable";
+
+    @Override
+    public boolean onCreate() {
+        Log.d(TAG, "onCreate called: mDbHelper = new InMemoryMmsProviderDbHelper()");
+        mOpenHelper = new InMemoryMmsProviderDbHelper();
+        return true;
+    }
+
+    // close mDbHelper database object
+    protected void closeDatabase() {
+        mOpenHelper.close();
+    }
+
+    /**
+     * An in memory DB for MmsProviderTestable to use
+     */
+    public static class InMemoryMmsProviderDbHelper extends SQLiteOpenHelper {
+
+
+        public InMemoryMmsProviderDbHelper() {
+            super(null,      // no context is needed for in-memory db
+                  null,      // db file name is null for in-memory db
+                  null,      // CursorFactory is null by default
+                  1);        // db version is no-op for tests
+            Log.d(TAG, "InMemoryMmsProviderDbHelper creating in-memory database");
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            // Set up the mms tables
+            Log.d(TAG, "InMemoryMmsProviderDbHelper onCreate creating the mms tables");
+            db.execSQL(CREATE_PDU_TABLE_STR);
+            db.execSQL(CREATE_ADDR_TABLE_STR);
+            db.execSQL(CREATE_PART_TABLE_STR);
+            db.execSQL(CREATE_RATE_TABLE_STR);
+            db.execSQL(CREATE_DRM_TABLE_STR);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.d(TAG, "InMemorySmsProviderDbHelper onUpgrade doing nothing");
+        }
+    }
+}
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTest.java b/tests/src/com/android/providers/telephony/SmsProviderTest.java
index 2bc5f0f..6a225af 100644
--- a/tests/src/com/android/providers/telephony/SmsProviderTest.java
+++ b/tests/src/com/android/providers/telephony/SmsProviderTest.java
@@ -16,6 +16,11 @@
 
 package com.android.providers.telephony;
 
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -26,6 +31,8 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
+import android.os.Process;
 import android.provider.Telephony;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
@@ -33,11 +40,14 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import junit.framework.TestCase;
 
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.Mockito;
-
+import org.mockito.MockitoAnnotations;
 
 /**
  * Tests for testing CRUD operations of SmsProvider.
@@ -54,9 +64,11 @@
 public class SmsProviderTest extends TestCase {
     private static final String TAG = "SmsProviderTest";
 
-    private MockContextWithProvider mContext;
+    @Mock private Context mContext;
     private MockContentResolver mContentResolver;
     private SmsProviderTestable mSmsProviderTestable;
+    @Mock private PackageManager mPackageManager;
+    @Mock private Resources mMockResources;
 
     private int notifyChangeCount;
 
@@ -75,79 +87,55 @@
     private final Uri mRawUriPermanentDelete =
             Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete");
 
-    /**
-     * This is used to give the SmsProviderTest a mocked context which takes a
-     * SmsProvider and attaches it to the ContentResolver with telephony authority.
-     * The mocked context also gives WRITE_APN_SETTINGS permissions
-     */
-    private class MockContextWithProvider extends MockContext {
-        private final MockContentResolver mResolver;
-
-        public MockContextWithProvider(SmsProvider smsProvider) {
-            mResolver = new MockContentResolver() {
-                @Override
-                public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
-                        int userHandle) {
-                    notifyChangeCount++;
-                }
-            };
-
-            // Add authority="sms" to given smsProvider
-            ProviderInfo providerInfo = new ProviderInfo();
-            providerInfo.authority = "sms";
-
-            // Add context to given smsProvider
-            smsProvider.attachInfoForTesting(this, providerInfo);
-            Log.d(TAG, "MockContextWithProvider: smsProvider.getContext(): "
-                    + smsProvider.getContext());
-
-            // Add given SmsProvider to mResolver with authority="sms" so that
-            // mResolver can send queries to mSmsProvider
-            mResolver.addProvider("sms", smsProvider);
-            Log.d(TAG, "MockContextWithProvider: Add SmsProvider to mResolver");
-        }
-
-        @Override
-        public Object getSystemService(String name) {
-            Log.d(TAG, "getSystemService: returning null");
-            switch (name) {
-                case Context.APP_OPS_SERVICE:
-                    return Mockito.mock(AppOpsManager.class);
-                case Context.TELEPHONY_SERVICE:
-                    return Mockito.mock(TelephonyManager.class);
-                default:
-                    return null;
-            }
-        }
-
-        @Override
-        public Resources getResources() {
-            Log.d(TAG, "getResources: returning null");
-            return null;
-        }
-
-        @Override
-        public int getUserId() {
-            return 0;
-        }
-
-        @Override
-        public MockContentResolver getContentResolver() {
-            return mResolver;
-        }
-
-        @Override
-        public int checkCallingOrSelfPermission(String permission) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
-    }
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        MockitoAnnotations.initMocks(this);
         mSmsProviderTestable = new SmsProviderTestable();
-        mContext = new MockContextWithProvider(mSmsProviderTestable);
-        mContentResolver = mContext.getContentResolver();
+
+        when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE)))
+                .thenReturn(mock(AppOpsManager.class));
+        when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE)))
+                .thenReturn(mock(TelephonyManager.class));
+
+        when(mContext.checkCallingOrSelfPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        when(mMockResources
+                        .getString(eq(com.android.internal.R.string.config_systemBluetoothStack)))
+                .thenReturn("com.android.bluetooth");
+        when(mContext.getResources()).thenReturn(mMockResources);
+        when(mContext.getUserId()).thenReturn(0);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        /**
+         * This is used to give the SmsProviderTest a mocked context which takes a
+         * SmsProvider and attaches it to the ContentResolver with telephony authority.
+         * The mocked context also gives WRITE_APN_SETTINGS permissions
+         */
+        mContentResolver = new MockContentResolver() {
+            @Override
+            public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
+                    int userHandle) {
+                notifyChangeCount++;
+            }
+        };
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+        // Add authority="sms" to given smsProvider
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = "sms";
+
+        // Add context to given smsProvider
+        mSmsProviderTestable.attachInfoForTesting(mContext, providerInfo);
+        Log.d(TAG, "MockContextWithProvider: smsProvider.getContext(): "
+                + mSmsProviderTestable.getContext());
+
+        // Add given SmsProvider to mResolver with authority="sms" so that
+        // mResolver can send queries to mSmsProvider
+        mContentResolver.addProvider("sms", mSmsProviderTestable);
+        Log.d(TAG, "MockContextWithProvider: Add SmsProvider to mResolver");
         notifyChangeCount = 0;
     }
 
diff --git a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
index f6a9c7f..bf0f49b 100644
--- a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertArrayEquals;
 
 import android.annotation.TargetApi;
-import android.app.backup.BackupDataOutput;
 import android.app.backup.FullBackupDataOutput;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -43,6 +42,7 @@
 import android.util.JsonWriter;
 import android.util.SparseArray;
 
+import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.internal.telephony.PhoneFactory;
 
 import libcore.io.IoUtils;
@@ -54,13 +54,11 @@
 import org.json.JSONObject;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -652,13 +650,21 @@
 
     /**
      * Test restore mms with the empty json array "[]".
-     * @throws Exception
      */
-    public void testRestoreMms_NoMms() throws Exception {
+    public void testRestoreMms_NoMms() {
         JsonReader jsonReader = new JsonReader(new StringReader(EMPTY_JSON_ARRAY));
         FakeMmsProvider mmsProvider = new FakeMmsProvider(null);
         mMockContentResolver.addProvider("mms", mmsProvider);
-        mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+        ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyBackupAgent, (agent) -> {
+                    try {
+                        agent.putMmsMessagesToProvider(jsonReader);
+                    } catch (IOException e) {
+                        fail("Encountered exception: " + e);
+                    }
+                    return null;
+                }
+        );
         assertEquals(0, mmsProvider.getRowsAdded());
     }
 
@@ -670,7 +676,16 @@
         JsonReader jsonReader = new JsonReader(new StringReader(addRandomDataToJson(mAllMmsJson)));
         FakeMmsProvider mmsProvider = new FakeMmsProvider(mMmsAllContentValues);
         mMockContentResolver.addProvider("mms", mmsProvider);
-        mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+        ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyBackupAgent, (agent) -> {
+                    try {
+                        agent.putMmsMessagesToProvider(jsonReader);
+                    } catch (IOException e) {
+                        fail("Encountered exception: " + e);
+                    }
+                    return null;
+                }
+        );
         assertEquals(18, mmsProvider.getRowsAdded());
         assertEquals(mThreadProvider.mIsThreadArchived, mThreadProvider.mUpdateThreadsArchived);
     }
@@ -684,7 +699,16 @@
                 (new StringReader(addRandomDataToJson(mMmsAllAttachmentJson)));
         FakeMmsProvider mmsProvider = new FakeMmsProvider(mMmsAllContentValues);
         mMockContentResolver.addProvider("mms", mmsProvider);
-        mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+        ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyBackupAgent, (agent) -> {
+                    try {
+                        agent.putMmsMessagesToProvider(jsonReader);
+                    } catch (IOException e) {
+                        fail("Encountered exception: " + e);
+                    }
+                    return null;
+                }
+        );
         assertEquals(7, mmsProvider.getRowsAdded());
     }
 
@@ -694,7 +718,16 @@
         FakeMmsProvider mmsProvider = new FakeMmsProvider(mMmsNullBodyContentValues);
         mMockContentResolver.addProvider("mms", mmsProvider);
 
-        mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+        ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyBackupAgent, (agent) -> {
+                    try {
+                        agent.putMmsMessagesToProvider(jsonReader);
+                    } catch (IOException e) {
+                        fail("Encountered exception: " + e);
+                    }
+                    return null;
+                }
+        );
 
         assertEquals(3, mmsProvider.getRowsAdded());
     }
@@ -943,6 +976,8 @@
             for (String key : modifiedValues.keySet()) {
                 assertEquals("Key:"+key, modifiedValues.get(key), values.get(key));
             }
+            values.remove(TelephonyBackupAgent.NOTIFY); // notify gets removed before final values
+
             assertEquals(modifiedValues.size(), values.size());
             return retUri;
         }
diff --git a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
index 5a4a7ba..b4a319c 100644
--- a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -231,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
@@ -263,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
@@ -317,6 +317,23 @@
     }
 
     @Test
+    public void databaseHelperOnUpgrade_hasMessageReferenceField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasMessageReferenceField");
+        // (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
+        // Telephony.SimInfo.COLUMN_TP_MESSAGE_REF
+        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_TP_MESSAGE_REF));
+    }
+
+    @Test
     public void databaseHelperOnUpgrade_hasLingeringNetworkTypeAlwaysOnMtuFields() {
         Log.d(TAG, "databaseHelperOnUpgrade_hasLingeringNetworkTypeAlwaysOnMtuFields");
         // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
@@ -335,6 +352,39 @@
         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));
+    }
+
+    @Test
+    public void databaseHelperOnUpgrade_hasUserHandleField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasUserHandleField");
+        // (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
+        // Telephony.SimInfo.COLUMN_USER_HANDLE
+        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_USER_HANDLE));
+    }
+
     /**
      * 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 2e48989..7269186 100644
--- a/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
@@ -48,7 +48,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.Log;
-
+import com.android.internal.telephony.LocalLog;
 import androidx.test.InstrumentationRegistry;
 
 import junit.framework.TestCase;
@@ -64,7 +64,9 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.stream.IntStream;
 
@@ -215,6 +217,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 +228,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 +237,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 +358,14 @@
         when(mockContextResources.getStringArray(anyInt())).thenReturn(new String[]{"ca", "us"});
         notifyChangeCount = 0;
         notifyChangeRestoreCount = 0;
+        // Required to access SIMINFO table
+        mTelephonyProviderTestable.fakeCallingUid(Process.PHONE_UID);
+        // Ignore local log during test
+        Field field = PhoneFactory.class.getDeclaredField("sLocalLogs");
+        field.setAccessible(true);
+        HashMap<String, LocalLog> localLogs = new HashMap<>();
+        localLogs.put("TelephonyProvider", new LocalLog(0));
+        field.set(null, localLogs);
     }
 
     private void setUpMockContext(boolean isActiveSubId) {
@@ -420,9 +432,9 @@
         };
         final String selection = Carriers.NUMERIC + "=?";
         String[] selectionArgs = { insertNumeric };
-        Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+        Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
                 + "\ntestInsertCarriers selection: " + selection
-                + "\ntestInsertCarriers selectionArgs: " + selectionArgs);
+                + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
         Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
                 testProjection, selection, selectionArgs, null);
 
@@ -596,9 +608,9 @@
         };
         final String selection = Carriers.NUMERIC + "=?";
         String[] selectionArgs = { insertNumeric };
-        Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+        Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
                 + "\ntestInsertCarriers selection: " + selection
-                + "\ntestInsertCarriers selectionArgs: " + selectionArgs);
+                + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
         Cursor cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
 
         // verify that inserted values match results of query
@@ -614,7 +626,7 @@
         final String selectionToDelete = Carriers.NUMERIC + "=?";
         String[] selectionArgsToDelete = { insertNumeric };
         Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                + "testInsertCarriers selectionArgs: " + selectionArgs);
+                + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
         int numRowsDeleted = mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
         assertEquals(1, numRowsDeleted);
 
@@ -651,9 +663,9 @@
         };
         final String selection = Carriers.NUMERIC + "=?";
         String[] selectionArgs = { insertNumeric };
-        Log.d(TAG, "testInsertCarriers query projection: " + testProjection
+        Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
                 + "\ntestInsertCarriers selection: " + selection
-                + "\ntestInsertCarriers selectionArgs: " + selectionArgs);
+                + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
         Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
                 testProjection, selection, selectionArgs, null);
 
@@ -673,7 +685,7 @@
         final String selectionToDelete = Carriers.NUMERIC + "=?";
         String[] selectionArgsToDelete = { insertNumeric };
         Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                + "testInsertCarriers selectionArgs: " + selectionArgs);
+                + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
         int numRowsDeleted = mContentResolver.delete(Carriers.CONTENT_URI,
                 selectionToDelete, selectionArgsToDelete);
         assertEquals(1, numRowsDeleted);
@@ -702,6 +714,7 @@
         final String insertCardId = "exampleCardId";
         final int insertProfileClass = SubscriptionManager.PROFILE_CLASS_DEFAULT;
         final int insertPortIndex = 1;
+        final int insertUserHandle = 0;
         contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, insertSubId);
         contentValues.put(SubscriptionManager.DISPLAY_NAME, insertDisplayName);
         contentValues.put(SubscriptionManager.CARRIER_NAME, insertCarrierName);
@@ -709,6 +722,7 @@
         contentValues.put(SubscriptionManager.CARD_ID, insertCardId);
         contentValues.put(SubscriptionManager.PROFILE_CLASS, insertProfileClass);
         contentValues.put(SubscriptionManager.PORT_INDEX, insertPortIndex);
+        contentValues.put(SubscriptionManager.USER_HANDLE, insertUserHandle);
 
         Log.d(TAG, "testSimTable Inserting contentValues: " + contentValues);
         mContentResolver.insert(SimInfo.CONTENT_URI, contentValues);
@@ -721,11 +735,12 @@
             SubscriptionManager.CARD_ID,
             SubscriptionManager.PROFILE_CLASS,
             SubscriptionManager.PORT_INDEX,
+            SubscriptionManager.USER_HANDLE,
         };
         final String selection = SubscriptionManager.DISPLAY_NAME + "=?";
         String[] selectionArgs = { insertDisplayName };
         Log.d(TAG,"\ntestSimTable selection: " + selection
-                + "\ntestSimTable selectionArgs: " + selectionArgs.toString());
+                + "\ntestSimTable selectionArgs: " + Arrays.toString(selectionArgs));
         Cursor cursor = mContentResolver.query(SimInfo.CONTENT_URI,
                 testProjection, selection, selectionArgs, null);
 
@@ -738,16 +753,18 @@
         final String resultCardId = cursor.getString(2);
         final int resultProfileClass = cursor.getInt(3);
         final int resultPortIndex = cursor.getInt(4);
+        final int resultUserHandle = cursor.getInt(5);
         assertEquals(insertSubId, resultSubId);
         assertEquals(insertCarrierName, resultCarrierName);
         assertEquals(insertCardId, resultCardId);
         assertEquals(insertPortIndex, resultPortIndex);
+        assertEquals(insertUserHandle, resultUserHandle);
 
         // delete test content
         final String selectionToDelete = SubscriptionManager.DISPLAY_NAME + "=?";
         String[] selectionArgsToDelete = { insertDisplayName };
         Log.d(TAG, "testSimTable deleting selection: " + selectionToDelete
-                + "testSimTable selectionArgs: " + selectionArgs);
+                + "testSimTable selectionArgs: " + Arrays.toString(selectionArgs));
         int numRowsDeleted = mContentResolver.delete(SimInfo.CONTENT_URI,
                 selectionToDelete, selectionArgsToDelete);
         assertEquals(1, numRowsDeleted);
@@ -1667,7 +1684,6 @@
         assertEquals(1, cursor.getCount());
         cursor.moveToFirst();
         assertEquals(otherName, cursor.getString(0));
-        PhoneFactory.addLocalLog("TelephonyProvider", 1);
     }
 
     /**