Merge TP1A.221105.002 to aosp-master - DO NOT MERGE

Merged-In: I4f6343abbe6d798b2b6633bde1df62d28686a7f2
Merged-In: I3d64037ad882761223302c8fd8c77ac04b6c5f8c
Merged-In: I3d64037ad882761223302c8fd8c77ac04b6c5f8c

Change-Id: I9e5c22511b7fada8b5def1ae8f0a87c971ec7f3a
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/assets/latest_carrier_id/carrier_list.pb b/assets/latest_carrier_id/carrier_list.pb
index 7c46449..9871fa1 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 5b521f2..373da77 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 ff06545..214b1ef 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 881a66a..abfa92a 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 fd32451..edb2637 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 4a2a263..8b364ed 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 be5d8f5..5d0a1d0 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 ef71379..af3ee2f 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 11e2628..443f45b 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 fe1806f..83f859a 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/CarrierIdProvider.java b/src/com/android/providers/telephony/CarrierIdProvider.java
index 9b6c2c0..c48b8c1 100644
--- a/src/com/android/providers/telephony/CarrierIdProvider.java
+++ b/src/com/android/providers/telephony/CarrierIdProvider.java
@@ -433,8 +433,9 @@
             CarrierIdProto.CarrierAttribute attr, int index) {
         if (index > CARRIER_ATTR_END_IDX) {
             ContentValues carrier = new ContentValues(cv);
-            if (!cvs.contains(carrier))
-            cvs.add(carrier);
+            if (!cvs.contains(carrier)) {
+                cvs.add(carrier);
+            }
             return;
         }
         boolean found = false;
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 5ddd6ac..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;
@@ -49,6 +48,8 @@
 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;
 
@@ -72,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);
@@ -80,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.
      *
@@ -316,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);
@@ -1070,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 73ffa3b..d1ec297 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -714,79 +714,99 @@
         }
     }
 
+
+    @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 " +
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 e09fa72..26242d9 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -3822,8 +3822,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();
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/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/TelephonyProviderTest.java b/tests/src/com/android/providers/telephony/TelephonyProviderTest.java
index b3892be..a546360 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;
 
@@ -358,6 +360,12 @@
         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) {
@@ -424,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);
 
@@ -600,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
@@ -618,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);
 
@@ -655,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);
 
@@ -677,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);
@@ -729,7 +737,7 @@
         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);
 
@@ -751,7 +759,7 @@
         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);
@@ -1671,7 +1679,6 @@
         assertEquals(1, cursor.getCount());
         cursor.moveToFirst();
         assertEquals(otherName, cursor.getString(0));
-        PhoneFactory.addLocalLog("TelephonyProvider", 1);
     }
 
     /**