Handle null body for MMS in TelephonyBackupAgent

Treat MMS with body set to null as messages with empty body to avoid
crashing with NPE.

Bug: 120261211
Test: 1) atest TelephonyBackupAgentTest
      2) atest SmsBackupRestoreTest
      3) Remove the null check and verify that TelephonyBackupAgentTest
         fails.

Change-Id: Ie575f1b20fd1728ad49fbf133f8f4cc7d1a90796
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index 5f6eb10..3db1088 100644
--- a/src/com/android/providers/telephony/TelephonyBackupAgent.java
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -1155,8 +1155,12 @@
             values.put(Telephony.Mms.Part.NAME, srcName);
             values.put(Telephony.Mms.Part.CONTENT_ID, "<"+srcName+">");
             values.put(Telephony.Mms.Part.CONTENT_LOCATION, srcName);
-            values.put(Telephony.Mms.Part.CHARSET, mms.body.charSet);
-            values.put(Telephony.Mms.Part.TEXT, mms.body.text);
+
+            values.put(
+                    Telephony.Mms.Part.CHARSET,
+                    mms.body == null ? CharacterSets.DEFAULT_CHARSET : mms.body.charSet);
+            values.put(Telephony.Mms.Part.TEXT, mms.body == null ? "" : mms.body.text);
+
             if (mContentResolver.insert(partUri, values) == null) {
                 if (DEBUG) {
                     Log.e(TAG, "Could not insert body part");
diff --git a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
index 49106ee..37cf6e3 100644
--- a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
@@ -40,6 +40,8 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.google.android.mms.pdu.CharacterSets;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -74,6 +76,8 @@
     private final List<ContentValues> mMmsTable = new ArrayList<>();
     /* Table contains parts, addresses of mms */
     private final List<ContentValues> mMmsAllContentValues = new ArrayList<>();
+    /* Table contains parts, addresses of mms for null body test case */
+    private final List<ContentValues> mMmsNullBodyContentValues = new ArrayList<>();
     /* Cursors being used to access sms, mms tables */
     private FakeCursor mSmsCursor, mMmsCursor;
     /* Test data with sms and mms */
@@ -81,7 +85,7 @@
     /* Json representation for the test data */
     private String[] mSmsJson, mMmsJson, mMmsAttachmentJson;
     /* sms, mms json concatenated as json array */
-    private String mAllSmsJson, mAllMmsJson, mMmsAllAttachmentJson;
+    private String mAllSmsJson, mAllMmsJson, mMmsAllAttachmentJson, mMmsAllNullBodyJson;
 
     private StringWriter mStringWriter;
 
@@ -169,7 +173,7 @@
                 new String[]{"+111 (111) 11111111", "+11121212", "example@example.com",
                         "+999999999"} /*addresses*/,
                 3 /*threadId*/, false /*read*/, null /*smil*/, null /*attachmentTypes*/,
-                null /*attachmentFilenames*/);
+                null /*attachmentFilenames*/, mMmsAllContentValues);
 
         mMmsJson[0] = "{\"self_phone\":\"+111111111111111\",\"sub\":\"Subject 1\"," +
                 "\"date\":\"111111\",\"date_sent\":\"111112\",\"m_type\":\"3\",\"v\":\"17\"," +
@@ -191,7 +195,7 @@
                 121 /*body charset*/,
                 new String[]{"+7 (333) ", "example@example.com", "+999999999"} /*addresses*/,
                 4 /*threadId*/, true /*read*/, null /*smil*/, null /*attachmentTypes*/,
-                null /*attachmentFilenames*/);
+                null /*attachmentFilenames*/, mMmsAllContentValues);
         mMmsJson[1] = "{\"date\":\"111122\",\"date_sent\":\"1111112\",\"m_type\":\"4\"," +
                 "\"v\":\"18\",\"msg_box\":\"222\",\"ct_l\":\"location 2\"," +
                 "\"recipients\":[\"example@example.com\",\"+999999999\"]," +
@@ -210,7 +214,7 @@
                 131 /*body charset*/,
                 new String[]{"333 333333333333", "+1232132214124"} /*addresses*/,
                 1 /*threadId*/, false /*read*/, null /*smil*/, null /*attachmentTypes*/,
-                null /*attachmentFilenames*/);
+                null /*attachmentFilenames*/, mMmsAllContentValues);
 
         mMmsJson[2] = "{\"self_phone\":\"+333333333333333\",\"sub\":\"Subject 10\"," +
                 "\"date\":\"111133\",\"date_sent\":\"1111132\",\"m_type\":\"5\",\"v\":\"19\"," +
@@ -239,7 +243,7 @@
                         + " width='100%'/></layout></head><body><par dur='5000ms'>"
                         + "<img src='image000000.jpg' region='Image' /></par></body></smil>",
                 new String[] {"image/jpg"} /*attachmentTypes*/,
-                new String[] {"GreatPict.jpg"}  /*attachmentFilenames*/);
+                new String[] {"GreatPict.jpg"}  /*attachmentFilenames*/, mMmsAllContentValues);
 
         mMmsAttachmentJson[0] = "{\"self_phone\":\"+111111111111111\",\"sub\":\"Subject 1\"," +
                 "\"date\":\"111111\",\"date_sent\":\"111112\",\"m_type\":\"3\",\"v\":\"17\"," +
@@ -255,6 +259,20 @@
 
         mMmsAllAttachmentJson = makeJsonArray(mMmsAttachmentJson);
 
+        createMmsRow(10 /*id*/, 1 /*subid*/, "Subject 1" /*subject*/,
+                100 /*subcharset*/, 111111 /*date*/, 111112 /*datesent*/, 3 /*type*/,
+                17 /*version*/, 0 /*textonly*/,
+                11 /*msgBox*/, "location 1" /*contentLocation*/, "" /*body*/,
+                CharacterSets.DEFAULT_CHARSET /*body charset*/, new String[] {} /*addresses*/,
+                3 /*threadId*/, false /*read*/, null /*smil*/, null /*attachmentTypes*/,
+                null /*attachmentFilenames*/, mMmsNullBodyContentValues);
+
+        mMmsAllNullBodyJson = makeJsonArray(new String[] {"{\"self_phone\":\"+111111111111111\"," +
+                "\"sub\":\"Subject 1\",\"date\":\"111111\",\"date_sent\":\"111112\",\"m_type\":" +
+                "\"3\",\"v\":\"17\",\"msg_box\":\"11\",\"ct_l\":\"location 1\"," +
+                "\"recipients\":[\"+11121212\",\"example@example.com\",\"+999999999\"]," +
+                "\"read\":\"0\", \"mms_addresses\":[],\"mms_charset\":111,\"sub_cs\":\"100\"}"});
+
 
         ContentProvider contentProvider = new MockContentProvider() {
             @Override
@@ -341,7 +359,8 @@
                                        String contentLocation, String body,
                                        int bodyCharset, String[] addresses, long threadId,
                                        boolean read, String smil, String[] attachmentTypes,
-                                       String[] attachmentFilenames) {
+                                       String[] attachmentFilenames,
+                                       List<ContentValues> rowsContainer) {
         ContentValues mmsRow = new ContentValues();
         mmsRow.put(Telephony.Mms._ID, id);
         mmsRow.put(Telephony.Mms.SUBSCRIPTION_ID, subId);
@@ -364,8 +383,8 @@
         final Uri partUri = Telephony.Mms.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).
                 appendPath("part").build();
         mCursors.put(partUri, createBodyCursor(body, bodyCharset, smil, attachmentTypes,
-                attachmentFilenames));
-        mMmsAllContentValues.add(mmsRow);
+                attachmentFilenames, rowsContainer));
+        rowsContainer.add(mmsRow);
 
         final Uri addrUri = Telephony.Mms.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).
                 appendPath("addr").build();
@@ -380,7 +399,8 @@
 
     // Cursor with parts of Mms.
     private FakeCursor createBodyCursor(String body, int charset, String existingSmil,
-            String[] attachmentTypes, String[] attachmentFilenames) {
+            String[] attachmentTypes, String[] attachmentFilenames,
+            List<ContentValues> rowsContainer) {
         List<ContentValues> table = new ArrayList<>();
         final String srcName = String.format("text.%06d.txt", 0);
         final String smilBody = TextUtils.isEmpty(existingSmil) ?
@@ -395,7 +415,7 @@
         smilPart.put(Telephony.Mms.Part.CONTENT_ID, "<smil>");
         smilPart.put(Telephony.Mms.Part.CONTENT_LOCATION, "smil.xml");
         smilPart.put(Telephony.Mms.Part.TEXT, smil);
-        mMmsAllContentValues.add(smilPart);
+        rowsContainer.add(smilPart);
 
         // Text part
         final ContentValues bodyPart = new ContentValues();
@@ -407,7 +427,7 @@
         bodyPart.put(Telephony.Mms.Part.CHARSET, charset);
         bodyPart.put(Telephony.Mms.Part.TEXT, body);
         table.add(bodyPart);
-        mMmsAllContentValues.add(bodyPart);
+        rowsContainer.add(bodyPart);
 
         // Attachments
         if (attachmentTypes != null) {
@@ -421,7 +441,7 @@
                 attachmentPart.put(Telephony.Mms.Part.CONTENT_ID, "<"+attachmentFilename+">");
                 attachmentPart.put(Telephony.Mms.Part.CONTENT_LOCATION, attachmentFilename);
                 table.add(attachmentPart);
-                mMmsAllContentValues.add(attachmentPart);
+                rowsContainer.add(attachmentPart);
             }
         }
 
@@ -622,6 +642,17 @@
         assertEquals(7, mmsProvider.getRowsAdded());
     }
 
+    public void testRestoreMmsWithNullBody() throws Exception {
+        JsonReader jsonReader = new JsonReader
+                (new StringReader(addRandomDataToJson(mMmsAllNullBodyJson)));
+        FakeMmsProvider mmsProvider = new FakeMmsProvider(mMmsNullBodyContentValues);
+        mMockContentResolver.addProvider("mms", mmsProvider);
+
+        mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+
+        assertEquals(3, mmsProvider.getRowsAdded());
+    }
+
     /**
      * Test with quota exceeded. Checking size of the backup before it hits quota and after.
      * It still backs up more than a quota since there is meta-info which matters with small amounts