Fix bug of permanently deleting SmsProvider rows doesn't work.

When SmsProvider#delete is called to delete rows permanently, it
directs to the wrong database thus always fails. This change is to
fix the problem.

Bug: 77974098
Test: unittest
Change-Id: Ic2235daeec01f75e8666d3af85a3581aa61931ea
Merged-In: Ic2235daeec01f75e8666d3af85a3581aa61931ea
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 4b84fe5..da371bf 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -889,30 +889,32 @@
             "content_url TEXT," +
             "offset INTEGER);";
 
+    /**
+     * This table is used by the SMS dispatcher to hold
+     * incomplete partial messages until all the parts arrive.
+     */
+    @VisibleForTesting
+    public static String CREATE_RAW_TABLE_STRING =
+            "CREATE TABLE raw (" +
+            "_id INTEGER PRIMARY KEY," +
+            "date INTEGER," +
+            "reference_number INTEGER," + // one per full message
+            "count INTEGER," + // the number of parts
+            "sequence INTEGER," + // the part number of this message
+            "destination_port INTEGER," +
+            "address TEXT," +
+            "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
+            "pdu TEXT," + // the raw PDU for this part
+            "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted
+            "message_body TEXT," + // message body
+            "display_originating_addr TEXT);";
+    // email address if from an email gateway, otherwise same as address
     private void createSmsTables(SQLiteDatabase db) {
         // N.B.: Whenever the columns here are changed, the columns in
         // {@ref MmsSmsProvider} must be changed to match.
         db.execSQL(CREATE_SMS_TABLE_STRING);
 
-        /**
-         * This table is used by the SMS dispatcher to hold
-         * incomplete partial messages until all the parts arrive.
-         */
-        db.execSQL("CREATE TABLE raw (" +
-                   "_id INTEGER PRIMARY KEY," +
-                   "date INTEGER," +
-                   "reference_number INTEGER," + // one per full message
-                   "count INTEGER," + // the number of parts
-                   "sequence INTEGER," + // the part number of this message
-                   "destination_port INTEGER," +
-                   "address TEXT," +
-                   "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
-                   "pdu TEXT," + // the raw PDU for this part
-                   "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted
-                   "message_body TEXT," + // message body
-                   "display_originating_addr TEXT);"
-                   // email address if from an email gateway, otherwise same as address
-        );
+        db.execSQL(CREATE_RAW_TABLE_STRING);
 
         db.execSQL(CREATE_ATTACHMENTS_TABLE_STRING);
 
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 37a1044..2b40d7e 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -43,6 +43,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -90,6 +92,8 @@
     @Override
     public boolean onCreate() {
         setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
+        // So we have two database files. One in de, one in ce. Here only "raw" table is in
+        // mDeOpenHelper, other tables are all in mCeOpenHelper.
         mDeOpenHelper = MmsSmsDatabaseHelper.getInstanceForDe(getContext());
         mCeOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
         TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
@@ -280,7 +284,8 @@
     }
 
     private SQLiteOpenHelper getDBOpenHelper(int match) {
-        if (match == SMS_RAW_MESSAGE) {
+        // Raw table is stored on de database. Other tables are stored in ce database.
+        if (match == SMS_RAW_MESSAGE || match == SMS_RAW_MESSAGE_PERMANENT_DELETE) {
             return mDeOpenHelper;
         }
         return mCeOpenHelper;
@@ -843,9 +848,12 @@
     }
 
     // Db open helper for tables stored in CE(Credential Encrypted) storage.
-    private SQLiteOpenHelper mCeOpenHelper;
-    // Db open helper for tables stored in DE(Device Encrypted) storage.
-    private SQLiteOpenHelper mDeOpenHelper;
+    @VisibleForTesting
+    public SQLiteOpenHelper mCeOpenHelper;
+    // Db open helper for tables stored in DE(Device Encrypted) storage. It's currently only used
+    // to store raw table.
+    @VisibleForTesting
+    public SQLiteOpenHelper mDeOpenHelper;
 
     private final static String TAG = "SmsProvider";
     private final static String VND_ANDROID_SMS = "vnd.android.cursor.item/sms";
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTest.java b/tests/src/com/android/providers/telephony/SmsProviderTest.java
index 8fba85b..ba63203 100644
--- a/tests/src/com/android/providers/telephony/SmsProviderTest.java
+++ b/tests/src/com/android/providers/telephony/SmsProviderTest.java
@@ -17,12 +17,14 @@
 package com.android.providers.telephony;
 
 import android.app.AppOpsManager;
+import android.content.ContentResolver;
 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.database.Cursor;
 import android.net.Uri;
 import android.provider.Telephony;
 import android.telephony.TelephonyManager;
@@ -58,6 +60,20 @@
 
     private int notifyChangeCount;
 
+    private final String mFakePdu = "123abc";
+    private final String mFakeAddress = "FakeAddress";
+    private final String mFakeOriginatingAddr = "FakeDisplayAddress";
+    private final String mFakeMessageBody = "FakeMessageBody";
+    private final int mFakeRefNumber = 123;
+    private final int mFakeSequence = 1;
+    private final int mFakeCount = 1;
+    private final int mFakePort = 1 << 19;
+    private final long mDate = 0;
+
+    private final Uri mRawUri =
+            Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
+    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
@@ -170,4 +186,86 @@
         assertEquals(Uri.parse("content://sms/attachments/1"),
                 mContentResolver.insert(Uri.parse("content://sms/attachments"), values));
     }
-}
\ No newline at end of file
+
+    @Test
+    @SmallTest
+    public void testRawTableInsert() {
+        // insert test contentValues
+        assertEquals(Uri.parse("content://sms/raw/1"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+
+        // Query and confirm contents.
+        Cursor cursor = mContentResolver.query(mRawUri, null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToNext();
+        assertEquals(mFakePdu, cursor.getString(cursor.getColumnIndex("pdu")));
+        assertEquals(mFakeAddress, cursor.getString(cursor.getColumnIndex("address")));
+        assertEquals(mFakeOriginatingAddr,
+                cursor.getString(cursor.getColumnIndex("display_originating_addr")));
+        assertEquals(mFakeMessageBody, cursor.getString(cursor.getColumnIndex("message_body")));
+        assertEquals(mDate, cursor.getInt(cursor.getColumnIndex("date")));
+        assertEquals(mFakePort, cursor.getInt(cursor.getColumnIndex("destination_port")));
+        assertEquals(mFakeRefNumber, cursor.getInt(cursor.getColumnIndex("reference_number")));
+        assertEquals(mFakeSequence, cursor.getInt(cursor.getColumnIndex("sequence")));
+        assertEquals(mFakeCount, cursor.getInt(cursor.getColumnIndex("count")));
+        assertEquals(0, cursor.getInt(cursor.getColumnIndex("deleted")));
+
+        // Insert another two.
+        assertEquals(Uri.parse("content://sms/raw/2"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+        assertEquals(Uri.parse("content://sms/raw/3"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+
+        cursor.close();
+    }
+
+    @Test
+    @SmallTest
+    public void testRawTableDelete() throws Exception {
+        assertEquals(Uri.parse("content://sms/raw/1"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+
+        // Mark as deleted.
+        String where = "reference_number=?";
+        String[] whereArgs = {Integer.toString(mFakeRefNumber)};
+        assertEquals(1, mContentResolver.delete(mRawUri, where, whereArgs));
+
+        // The row should still be in table, with column "deleted" to be 1.
+        Cursor cursor = mSmsProviderTestable.mDeOpenHelper.getReadableDatabase().query(
+                "raw", null, null, null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToNext();
+        assertEquals(1, cursor.getInt(cursor.getColumnIndex("deleted")));
+        cursor.close();
+
+        // The deleted row should be purged.
+        cursor = mContentResolver.query(mRawUri, null, null, null, null);
+        assertEquals(0, cursor.getCount());
+
+        // Permanent delete all rows.
+        assertEquals(Uri.parse("content://sms/raw/1"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+        assertEquals(Uri.parse("content://sms/raw/2"),
+                mContentResolver.insert(mRawUri, getFakeRawValue()));
+        assertEquals(2, mContentResolver.delete(mRawUriPermanentDelete, null, null));
+        cursor = mSmsProviderTestable.mDeOpenHelper.getReadableDatabase().query(
+                "raw", null, null, null, null, null, null);
+        assertEquals(0, cursor.getCount());
+        cursor.close();
+    }
+
+    private ContentValues getFakeRawValue() {
+        ContentValues values = new ContentValues();
+        values.put("pdu", mFakePdu);
+        values.put("date", mDate);
+        values.put("destination_port", mFakePort);
+        values.put("address", mFakeAddress);
+        values.put("display_originating_addr", mFakeOriginatingAddr);
+        values.put("reference_number", mFakeRefNumber);
+        values.put("sequence", mFakeSequence);
+        values.put("count", mFakeCount);
+        values.put("message_body", mFakeMessageBody);
+
+        return values;
+    }
+}
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTestable.java b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
index 9c3372e..3c077fa 100644
--- a/tests/src/com/android/providers/telephony/SmsProviderTestable.java
+++ b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
@@ -25,30 +25,18 @@
 public class SmsProviderTestable extends SmsProvider {
     private static final String TAG = "SmsProviderTestable";
 
-    private InMemorySmsProviderDbHelper mDbHelper;
-
     @Override
     public boolean onCreate() {
         Log.d(TAG, "onCreate called: mDbHelper = new InMemorySmsProviderDbHelper()");
-        mDbHelper = new InMemorySmsProviderDbHelper();
+        mCeOpenHelper = new InMemorySmsProviderDbHelper();
+        mDeOpenHelper = new InMemorySmsProviderDbHelper();
         return true;
     }
 
-    @Override
-    SQLiteDatabase getReadableDatabase(int match) {
-        Log.d(TAG, "getReadableDatabase called");
-        return mDbHelper.getReadableDatabase();
-    }
-
-    @Override
-    SQLiteDatabase getWritableDatabase(int match) {
-        Log.d(TAG, "getWritableDatabase called");
-        return mDbHelper.getWritableDatabase();
-    }
-
     // close mDbHelper database object
     protected void closeDatabase() {
-        mDbHelper.close();
+        mCeOpenHelper.close();
+        mDeOpenHelper.close();
     }
 
     /**
@@ -70,6 +58,7 @@
             // Set up the sms tables
             Log.d(TAG, "InMemorySmsProviderDbHelper onCreate creating the sms tables");
             db.execSQL(MmsSmsDatabaseHelper.CREATE_SMS_TABLE_STRING);
+            db.execSQL(MmsSmsDatabaseHelper.CREATE_RAW_TABLE_STRING);
             db.execSQL(MmsSmsDatabaseHelper.CREATE_ATTACHMENTS_TABLE_STRING);
         }