App crashed as it was moving to foreground and message was received

Bug 7540115

In the MmsSmsProvider, wrap the code that creates a new thread in a transaction.
Creating a new thread requires multiple database operations. It's possible
for the newly created thread to be deleted part of the way through the
operation.

Change-Id: I3cbf8ba4ad3f3f30b513280ac3defc276b4a70a0
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 036cad2..4f23be8 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -242,7 +242,7 @@
      * unreferenced rows from the canonical_addresses table.
      */
     private static void removeUnferencedCanonicalAddresses(SQLiteDatabase db) {
-        Cursor c = db.query("threads", new String[] { "recipient_ids" },
+        Cursor c = db.query(MmsSmsProvider.TABLE_THREADS, new String[] { "recipient_ids" },
                 null, null, null, null, null);
         if (c != null) {
             try {
@@ -293,7 +293,7 @@
             // Delete the row for this thread in the threads table if
             // there are no more messages attached to it in either
             // the sms or pdu tables.
-            int rows = db.delete("threads",
+            int rows = db.delete(MmsSmsProvider.TABLE_THREADS,
                       "_id = ? AND _id NOT IN" +
                       "          (SELECT thread_id FROM sms " +
                       "           UNION SELECT thread_id FROM pdu)",
@@ -405,7 +405,7 @@
             // TODO: there are several db operations in this function. Lets wrap them in a
             // transaction to make it faster.
             // remove orphaned threads
-            db.delete("threads",
+            db.delete(MmsSmsProvider.TABLE_THREADS,
                     "_id NOT IN (SELECT DISTINCT thread_id FROM sms where thread_id NOT NULL " +
                     "UNION SELECT DISTINCT thread_id FROM pdu where thread_id NOT NULL)", null);
 
@@ -1480,7 +1480,7 @@
 
         if (!sTriedAutoIncrement) {
             sTriedAutoIncrement = true;
-            boolean hasAutoIncrementThreads = hasAutoIncrement(db, "threads");
+            boolean hasAutoIncrementThreads = hasAutoIncrement(db, MmsSmsProvider.TABLE_THREADS);
             boolean hasAutoIncrementAddresses = hasAutoIncrement(db, "canonical_addresses");
             boolean hasAutoIncrementPart = hasAutoIncrement(db, "part");
             boolean hasAutoIncrementPdu = hasAutoIncrement(db, "pdu");
@@ -1619,7 +1619,7 @@
     // storage to make a copy of the threads table. That's ok. This upgrade is optional. It'll
     // be called again next time the device is rebooted.
     private void upgradeThreadsTableToAutoIncrement(SQLiteDatabase db) {
-        if (hasAutoIncrement(db, "threads")) {
+        if (hasAutoIncrement(db, MmsSmsProvider.TABLE_THREADS)) {
             Log.d(TAG, "[MmsSmsDb] upgradeThreadsTableToAutoIncrement: already upgraded");
             return;
         }
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 733a5e5..21dd188 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -105,6 +105,11 @@
      */
     private static final String TABLE_CANONICAL_ADDRESSES = "canonical_addresses";
 
+    /**
+     * the name of the table that is used to store the conversation threads.
+     */
+    static final String TABLE_THREADS = "threads";
+
     // These constants are used to construct union queries across the
     // MMS and SMS base tables.
 
@@ -598,7 +603,7 @@
         }
         values.put(ThreadsColumns.MESSAGE_COUNT, 0);
 
-        long result = mOpenHelper.getWritableDatabase().insert("threads", null, values);
+        long result = mOpenHelper.getWritableDatabase().insert(TABLE_THREADS, null, values);
         Log.d(LOG_TAG, "insertThread: created new thread_id " + result +
                 " for recipientIds " + /*recipientIds*/ "xxxxxxx");
 
@@ -637,24 +642,35 @@
         }
 
         String[] selectionArgs = new String[] { recipientIds };
+
         SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        Cursor cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
-
-        if (cursor.getCount() == 0) {
-            cursor.close();
-
-            Log.d(LOG_TAG, "getThreadId: create new thread_id for recipients " +
-                    /*recipients*/ "xxxxxxxx");
-            insertThread(recipientIds, recipients.size());
-
-            db = mOpenHelper.getReadableDatabase();  // In case insertThread closed it
+        db.beginTransaction();
+        Cursor cursor = null;
+        try {
+            // Find the thread with the given recipients
             cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
+
+            if (cursor.getCount() == 0) {
+                // No thread with those recipients exists, so create the thread.
+                cursor.close();
+
+                Log.d(LOG_TAG, "getThreadId: create new thread_id for recipients " +
+                        /*recipients*/ "xxxxxxxx");
+                insertThread(recipientIds, recipients.size());
+
+                // The thread was just created, now find it and return it.
+                cursor = db.rawQuery(THREAD_QUERY, selectionArgs);
+            }
+            db.setTransactionSuccessful();
+        } catch (Throwable ex) {
+            Log.e(LOG_TAG, ex.getMessage(), ex);
+        } finally {
+            db.endTransaction();
         }
 
-        if (cursor.getCount() > 1) {
+        if (cursor != null && cursor.getCount() > 1) {
             Log.w(LOG_TAG, "getThreadId: why is cursorCount=" + cursor.getCount());
         }
-
         return cursor;
     }
 
@@ -700,7 +716,7 @@
      */
     private Cursor getSimpleConversations(String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
-        return mOpenHelper.getReadableDatabase().query("threads", projection,
+        return mOpenHelper.getReadableDatabase().query(TABLE_THREADS, projection,
                 selection, selectionArgs, null, null, " date DESC");
     }
 
@@ -1005,7 +1021,7 @@
         String[] columns = handleNullThreadsProjection(projection);
 
         queryBuilder.setDistinct(true);
-        queryBuilder.setTables("threads");
+        queryBuilder.setTables(TABLE_THREADS);
         return queryBuilder.query(
                 mOpenHelper.getReadableDatabase(), columns, finalSelection,
                 selectionArgs, sortOrder, null, null);
@@ -1177,7 +1193,7 @@
                 MmsSmsDatabaseHelper.updateAllThreads(db, null, null);
                 break;
             case URI_OBSOLETE_THREADS:
-                affectedRows = db.delete("threads",
+                affectedRows = db.delete(TABLE_THREADS,
                         "_id NOT IN (SELECT DISTINCT thread_id FROM sms where thread_id NOT NULL " +
                         "UNION SELECT DISTINCT thread_id FROM pdu where thread_id NOT NULL)", null);
                 break;