summaryrefslogtreecommitdiffstats
path: root/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java')
-rw-r--r--tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java911
1 files changed, 911 insertions, 0 deletions
diff --git a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
new file mode 100644
index 0000000..4866dbd
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2016 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 android.annotation.TargetApi;
+import android.app.backup.FullBackupDataOutput;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.ContextWrapper;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.BaseColumns;
+import android.provider.Telephony;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockCursor;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.SparseArray;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+
+/**
+ * Tests for testing backup/restore of SMS and text MMS messages.
+ * For backup it creates fake provider and checks resulting json array.
+ * For restore provides json array and checks inserts of the messages into provider.
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class TelephonyBackupAgentTest extends AndroidTestCase {
+ /* Map subscriptionId -> phone number */
+ private SparseArray<String> mSubId2Phone;
+ /* Map phone number -> subscriptionId */
+ private ArrayMap<String, Integer> mPhone2SubId;
+ /* Table being used for sms cursor */
+ private final List<ContentValues> mSmsTable = new ArrayList<>();
+ /* Table begin used for mms cursor */
+ private final List<ContentValues> mMmsTable = new ArrayList<>();
+ /* Table contains parts, addresses of mms */
+ private final List<ContentValues> mMmsAllContentValues = new ArrayList<>();
+ /* Cursors being used to access sms, mms tables */
+ private FakeCursor mSmsCursor, mMmsCursor;
+ /* Test data with sms and mms */
+ private ContentValues[] mSmsRows, mMmsRows;
+ /* Json representation for the test data */
+ private String[] mSmsJson, mMmsJson;
+ /* sms, mms json concatenated as json array */
+ private String mAllSmsJson, mAllMmsJson;
+
+ private StringWriter mStringWriter;
+
+ /* Content resolver passed to the backupAgent */
+ private MockContentResolver mMockContentResolver = new MockContentResolver();
+
+ /* Map uri -> cursors. Being used for contentprovider. */
+ private Map<Uri, FakeCursor> mCursors;
+ /* Content provider with threadIds.*/
+ private ThreadProvider mThreadProvider = new ThreadProvider();
+
+ private static final String EMPTY_JSON_ARRAY = "[]";
+
+ TelephonyBackupAgent mTelephonyBackupAgent;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ /* Filling up subscription maps */
+ mStringWriter = new StringWriter();
+ mSubId2Phone = new SparseArray<String>();
+ mSubId2Phone.append(1, "+111111111111111");
+ mSubId2Phone.append(3, "+333333333333333");
+
+ mPhone2SubId = new ArrayMap<>();
+ for (int i=0; i<mSubId2Phone.size(); ++i) {
+ mPhone2SubId.put(mSubId2Phone.valueAt(i), mSubId2Phone.keyAt(i));
+ }
+
+ mCursors = new HashMap<Uri, FakeCursor>();
+ /* Bind tables to the cursors */
+ mSmsCursor = new FakeCursor(mSmsTable, TelephonyBackupAgent.SMS_PROJECTION);
+ mCursors.put(Telephony.Sms.CONTENT_URI, mSmsCursor);
+ mMmsCursor = new FakeCursor(mMmsTable, TelephonyBackupAgent.MMS_PROJECTION);
+ mCursors.put(Telephony.Mms.CONTENT_URI, mMmsCursor);
+
+
+ /* Generating test data */
+ mSmsRows = new ContentValues[4];
+ mSmsJson = new String[4];
+ mSmsRows[0] = createSmsRow(1, 1, "+1232132214124", "sms 1", "sms subject", 9087978987l,
+ 999999999, 3, 44, 1);
+ mSmsJson[0] = "{\"self_phone\":\"+111111111111111\",\"address\":" +
+ "\"+1232132214124\",\"body\":\"sms 1\",\"subject\":\"sms subject\",\"date\":" +
+ "\"9087978987\",\"date_sent\":\"999999999\",\"status\":\"3\",\"type\":\"44\"," +
+ "\"recipients\":[\"+123 (213) 2214124\"],\"archived\":true}";
+ mThreadProvider.setArchived(
+ mThreadProvider.getOrCreateThreadId(new String[]{"+123 (213) 2214124"}));
+
+ mSmsRows[1] = createSmsRow(2, 2, "+1232132214124", "sms 2", null, 9087978987l, 999999999,
+ 0, 4, 1);
+ mSmsJson[1] = "{\"address\":\"+1232132214124\",\"body\":\"sms 2\",\"date\":" +
+ "\"9087978987\",\"date_sent\":\"999999999\",\"status\":\"0\",\"type\":\"4\"," +
+ "\"recipients\":[\"+123 (213) 2214124\"]}";
+
+ mSmsRows[2] = createSmsRow(4, 3, "+1232221412433 +1232221412444", "sms 3", null,
+ 111111111111l, 999999999, 2, 3, 2);
+ mSmsJson[2] = "{\"self_phone\":\"+333333333333333\",\"address\":" +
+ "\"+1232221412433 +1232221412444\",\"body\":\"sms 3\",\"date\":\"111111111111\"," +
+ "\"date_sent\":" +
+ "\"999999999\",\"status\":\"2\",\"type\":\"3\"," +
+ "\"recipients\":[\"+1232221412433\",\"+1232221412444\"]}";
+ mThreadProvider.getOrCreateThreadId(new String[]{"+1232221412433", "+1232221412444"});
+
+
+ mSmsRows[3] = createSmsRow(5, 3, null, "sms 4", null,
+ 111111111111l, 999999999, 2, 3, 5);
+ mSmsJson[3] = "{\"self_phone\":\"+333333333333333\"," +
+ "\"body\":\"sms 4\",\"date\":\"111111111111\"," +
+ "\"date_sent\":" +
+ "\"999999999\",\"status\":\"2\",\"type\":\"3\"}";
+
+ mAllSmsJson = makeJsonArray(mSmsJson);
+
+
+
+ mMmsRows = new ContentValues[3];
+ mMmsJson = new String[3];
+ mMmsRows[0] = createMmsRow(1 /*id*/, 1 /*subid*/, "Subject 1" /*subject*/,
+ 100 /*subcharset*/, 111111 /*date*/, 111112 /*datesent*/, 3 /*type*/,
+ 17 /*version*/, 1 /*textonly*/,
+ 11 /*msgBox*/, "location 1" /*contentLocation*/, "MMs body 1" /*body*/,
+ 111 /*body charset*/,
+ new String[]{"+111 (111) 11111111", "+11121212", "example@example.com",
+ "+999999999"} /*addresses*/,
+ 3 /*threadId*/);
+
+ mMmsJson[0] = "{\"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\"]," +
+ "\"mms_addresses\":" +
+ "[{\"type\":10,\"address\":\"+111 (111) 11111111\",\"charset\":100}," +
+ "{\"type\":11,\"address\":\"+11121212\",\"charset\":101},{\"type\":12,\"address\":"+
+ "\"example@example.com\",\"charset\":102},{\"type\":13,\"address\":\"+999999999\"" +
+ ",\"charset\":103}],\"mms_body\":\"MMs body 1\",\"mms_charset\":111,\"" +
+ "sub_cs\":\"100\"}";
+ mThreadProvider.getOrCreateThreadId(new String[]{"+11121212", "example@example.com",
+ "+999999999"});
+
+ mMmsRows[1] = createMmsRow(2 /*id*/, 2 /*subid*/, null /*subject*/, 100 /*subcharset*/,
+ 111122 /*date*/, 1111112 /*datesent*/, 4 /*type*/, 18 /*version*/, 1 /*textonly*/,
+ 222 /*msgBox*/, "location 2" /*contentLocation*/, "MMs body 2" /*body*/,
+ 121 /*body charset*/,
+ new String[]{"+7 (333) ", "example@example.com", "+999999999"} /*addresses*/,
+ 4 /*threadId*/);
+ mMmsJson[1] = "{\"date\":\"111122\",\"date_sent\":\"1111112\",\"m_type\":\"4\"," +
+ "\"v\":\"18\",\"msg_box\":\"222\",\"ct_l\":\"location 2\"," +
+ "\"recipients\":[\"example@example.com\",\"+999999999\"]," +
+ "\"mms_addresses\":" +
+ "[{\"type\":10,\"address\":\"+7 (333) \",\"charset\":100}," +
+ "{\"type\":11,\"address\":\"example@example.com\",\"charset\":101}," +
+ "{\"type\":12,\"address\":\"+999999999\",\"charset\":102}]," +
+ "\"mms_body\":\"MMs body 2\",\"mms_charset\":121}";
+ mThreadProvider.getOrCreateThreadId(new String[]{"example@example.com", "+999999999"});
+
+ mMmsRows[2] = createMmsRow(9 /*id*/, 3 /*subid*/, "Subject 10" /*subject*/,
+ 10 /*subcharset*/, 111133 /*date*/, 1111132 /*datesent*/, 5 /*type*/,
+ 19 /*version*/, 1 /*textonly*/,
+ 333 /*msgBox*/, null /*contentLocation*/, "MMs body 3" /*body*/,
+ 131 /*body charset*/,
+ new String[]{"333 333333333333", "+1232132214124"} /*addresses*/,
+ 1 /*threadId*/);
+
+ mMmsJson[2] = "{\"self_phone\":\"+333333333333333\",\"sub\":\"Subject 10\"," +
+ "\"date\":\"111133\",\"date_sent\":\"1111132\",\"m_type\":\"5\",\"v\":\"19\"," +
+ "\"msg_box\":\"333\"," +
+ "\"recipients\":[\"+123 (213) 2214124\"],\"archived\":true," +
+ "\"mms_addresses\":" +
+ "[{\"type\":10,\"address\":\"333 333333333333\",\"charset\":100}," +
+ "{\"type\":11,\"address\":\"+1232132214124\",\"charset\":101}]," +
+ "\"mms_body\":\"MMs body 3\",\"mms_charset\":131," +
+ "\"sub_cs\":\"10\"}";
+ mAllMmsJson = makeJsonArray(mMmsJson);
+
+ ContentProvider contentProvider = new MockContentProvider() {
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (mCursors.containsKey(uri)) {
+ FakeCursor fakeCursor = mCursors.get(uri);
+ if (projection != null) {
+ fakeCursor.setProjection(projection);
+ }
+ fakeCursor.nextRow = 0;
+ return fakeCursor;
+ }
+ fail("No cursor for " + uri.toString());
+ return null;
+ }
+ };
+
+ mMockContentResolver.addProvider("sms", contentProvider);
+ mMockContentResolver.addProvider("mms", contentProvider);
+ mMockContentResolver.addProvider("mms-sms", mThreadProvider);
+
+ mTelephonyBackupAgent = new TelephonyBackupAgent();
+ mTelephonyBackupAgent.attach(new ContextWrapper(getContext()) {
+ @Override
+ public ContentResolver getContentResolver() {
+ return mMockContentResolver;
+ }
+ });
+
+
+ mTelephonyBackupAgent.clearSharedPreferences();
+ mTelephonyBackupAgent.setContentResolver(mMockContentResolver);
+ mTelephonyBackupAgent.setSubId(mSubId2Phone, mPhone2SubId);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mTelephonyBackupAgent.clearSharedPreferences();
+ super.tearDown();
+ }
+
+ private static String makeJsonArray(String[] json) {
+ StringBuilder stringBuilder = new StringBuilder("[");
+ for (int i=0; i<json.length; ++i) {
+ if (i > 0) {
+ stringBuilder.append(",");
+ }
+ stringBuilder.append(json[i]);
+ }
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+
+ private static ContentValues createSmsRow(int id, int subId, String address, String body,
+ String subj, long date, long dateSent,
+ int status, int type, long threadId) {
+ ContentValues smsRow = new ContentValues();
+ smsRow.put(Telephony.Sms._ID, id);
+ smsRow.put(Telephony.Sms.SUBSCRIPTION_ID, subId);
+ if (address != null) {
+ smsRow.put(Telephony.Sms.ADDRESS, address);
+ }
+ if (body != null) {
+ smsRow.put(Telephony.Sms.BODY, body);
+ }
+ if (subj != null) {
+ smsRow.put(Telephony.Sms.SUBJECT, subj);
+ }
+ smsRow.put(Telephony.Sms.DATE, String.valueOf(date));
+ smsRow.put(Telephony.Sms.DATE_SENT, String.valueOf(dateSent));
+ smsRow.put(Telephony.Sms.STATUS, String.valueOf(status));
+ smsRow.put(Telephony.Sms.TYPE, String.valueOf(type));
+ smsRow.put(Telephony.Sms.THREAD_ID, threadId);
+
+ return smsRow;
+ }
+
+ private ContentValues createMmsRow(int id, int subId, String subj, int subCharset,
+ long date, long dateSent, int type, int version,
+ int textOnly, int msgBox,
+ String contentLocation, String body,
+ int bodyCharset, String[] addresses, long threadId) {
+ ContentValues mmsRow = new ContentValues();
+ mmsRow.put(Telephony.Mms._ID, id);
+ mmsRow.put(Telephony.Mms.SUBSCRIPTION_ID, subId);
+ if (subj != null) {
+ mmsRow.put(Telephony.Mms.SUBJECT, subj);
+ mmsRow.put(Telephony.Mms.SUBJECT_CHARSET, String.valueOf(subCharset));
+ }
+ mmsRow.put(Telephony.Mms.DATE, String.valueOf(date));
+ mmsRow.put(Telephony.Mms.DATE_SENT, String.valueOf(dateSent));
+ mmsRow.put(Telephony.Mms.MESSAGE_TYPE, String.valueOf(type));
+ mmsRow.put(Telephony.Mms.MMS_VERSION, String.valueOf(version));
+ mmsRow.put(Telephony.Mms.TEXT_ONLY, textOnly);
+ mmsRow.put(Telephony.Mms.MESSAGE_BOX, String.valueOf(msgBox));
+ if (contentLocation != null) {
+ mmsRow.put(Telephony.Mms.CONTENT_LOCATION, contentLocation);
+ }
+ mmsRow.put(Telephony.Mms.THREAD_ID, threadId);
+
+ final Uri partUri = Telephony.Mms.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).
+ appendPath("part").build();
+ mCursors.put(partUri, createBodyCursor(body, bodyCharset));
+ mMmsAllContentValues.add(mmsRow);
+
+ final Uri addrUri = Telephony.Mms.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).
+ appendPath("addr").build();
+ mCursors.put(addrUri, createAddrCursor(addresses));
+
+ return mmsRow;
+ }
+
+ private static final String APP_SMIL = "application/smil";
+ private static final String TEXT_PLAIN = "text/plain";
+
+ // Cursor with parts of Mms.
+ private FakeCursor createBodyCursor(String body, int charset) {
+ List<ContentValues> table = new ArrayList<>();
+ final String srcName = String.format("text.%06d.txt", 0);
+ final String smilBody = String.format(TelephonyBackupAgent.sSmilTextPart, srcName);
+ final String smil = String.format(TelephonyBackupAgent.sSmilTextOnly, smilBody);
+
+ final ContentValues smilPart = new ContentValues();
+ smilPart.put(Telephony.Mms.Part.SEQ, -1);
+ smilPart.put(Telephony.Mms.Part.CONTENT_TYPE, APP_SMIL);
+ smilPart.put(Telephony.Mms.Part.NAME, "smil.xml");
+ 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);
+
+ final ContentValues bodyPart = new ContentValues();
+ bodyPart.put(Telephony.Mms.Part.SEQ, 0);
+ bodyPart.put(Telephony.Mms.Part.CONTENT_TYPE, TEXT_PLAIN);
+ bodyPart.put(Telephony.Mms.Part.NAME, srcName);
+ bodyPart.put(Telephony.Mms.Part.CONTENT_ID, "<"+srcName+">");
+ bodyPart.put(Telephony.Mms.Part.CONTENT_LOCATION, srcName);
+ bodyPart.put(Telephony.Mms.Part.CHARSET, charset);
+ bodyPart.put(Telephony.Mms.Part.TEXT, body);
+ table.add(bodyPart);
+ mMmsAllContentValues.add(bodyPart);
+
+ return new FakeCursor(table, TelephonyBackupAgent.MMS_TEXT_PROJECTION);
+ }
+
+ // Cursor with addresses of Mms.
+ private FakeCursor createAddrCursor(String[] addresses) {
+ List<ContentValues> table = new ArrayList<>();
+ for (int i=0; i<addresses.length; ++i) {
+ ContentValues addr = new ContentValues();
+ addr.put(Telephony.Mms.Addr.TYPE, 10+i);
+ addr.put(Telephony.Mms.Addr.ADDRESS, addresses[i]);
+ addr.put(Telephony.Mms.Addr.CHARSET, 100+i);
+ mMmsAllContentValues.add(addr);
+ table.add(addr);
+ }
+ return new FakeCursor(table, TelephonyBackupAgent.MMS_ADDR_PROJECTION);
+ }
+
+ /**
+ * Test with no sms in the provider.
+ * @throws Exception
+ */
+ public void testBackupSms_NoSms() throws Exception {
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(EMPTY_JSON_ARRAY, mStringWriter.toString());
+ }
+
+ /**
+ * Test with 3 sms in the provider with the limit per file 4.
+ * @throws Exception
+ */
+ public void testBackupSms_AllSms() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 4;
+ mSmsTable.addAll(Arrays.asList(mSmsRows));
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(mAllSmsJson, mStringWriter.toString());
+ }
+
+ /**
+ * Test with 3 sms in the provider with the limit per file 3.
+ * @throws Exception
+ */
+ public void testBackupSms_AllSmsWithExactFileLimit() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 4;
+ mSmsTable.addAll(Arrays.asList(mSmsRows));
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(mAllSmsJson, mStringWriter.toString());
+ }
+
+ /**
+ * Test with 3 sms in the provider with the limit per file 1.
+ * @throws Exception
+ */
+ public void testBackupSms_AllSmsOneMessagePerFile() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 1;
+ mSmsTable.addAll(Arrays.asList(mSmsRows));
+
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mSmsJson[0] + "]", mStringWriter.toString());
+
+ mStringWriter = new StringWriter();
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mSmsJson[1] + "]", mStringWriter.toString());
+
+ mStringWriter = new StringWriter();
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mSmsJson[2] + "]", mStringWriter.toString());
+
+ mStringWriter = new StringWriter();
+ mTelephonyBackupAgent.putSmsMessagesToJson(mSmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mSmsJson[3] + "]", mStringWriter.toString());
+ }
+
+ /**
+ * Test with no mms in the pvovider.
+ * @throws Exception
+ */
+ public void testBackupMms_NoMms() throws Exception {
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(EMPTY_JSON_ARRAY, mStringWriter.toString());
+ }
+
+ /**
+ * Test with all mms.
+ * @throws Exception
+ */
+ public void testBackupMms_AllMms() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 4;
+ mMmsTable.addAll(Arrays.asList(mMmsRows));
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(mAllMmsJson, mStringWriter.toString());
+ }
+
+ /**
+ * Test with 3 mms in the provider with the limit per file 1.
+ * @throws Exception
+ */
+ public void testBackupMms_OneMessagePerFile() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 1;
+ mMmsTable.addAll(Arrays.asList(mMmsRows));
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mMmsJson[0] + "]", mStringWriter.toString());
+
+ mStringWriter = new StringWriter();
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mMmsJson[1] + "]", mStringWriter.toString());
+
+ mStringWriter = new StringWriter();
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals("[" + mMmsJson[2] + "]", mStringWriter.toString());
+ }
+
+ /**
+ * Test with 3 mms in the provider with the limit per file 3.
+ * @throws Exception
+ */
+ public void testBackupMms_WithExactFileLimit() throws Exception {
+ mMmsTable.addAll(Arrays.asList(mMmsRows));
+ mTelephonyBackupAgent.mMaxMsgPerFile = 3;
+ mTelephonyBackupAgent.putMmsMessagesToJson(mMmsCursor, new JsonWriter(mStringWriter));
+ assertEquals(mAllMmsJson, mStringWriter.toString());
+ }
+
+ /**
+ * Test restore sms with the empty json array "[]".
+ * @throws Exception
+ */
+ public void testRestoreSms_NoSms() throws Exception {
+ JsonReader jsonReader = new JsonReader(new StringReader(EMPTY_JSON_ARRAY));
+ FakeSmsProvider smsProvider = new FakeSmsProvider(null);
+ mMockContentResolver.addProvider("sms", smsProvider);
+ mTelephonyBackupAgent.putSmsMessagesToProvider(jsonReader);
+ assertEquals(0, smsProvider.getRowsAdded());
+ }
+
+ /**
+ * Test restore sms with three sms json object in the array.
+ * @throws Exception
+ */
+ public void testRestoreSms_AllSms() throws Exception {
+ mTelephonyBackupAgent.initUnknownSender();
+ JsonReader jsonReader = new JsonReader(new StringReader(addRandomDataToJson(mAllSmsJson)));
+ FakeSmsProvider smsProvider = new FakeSmsProvider(mSmsRows);
+ mMockContentResolver.addProvider("sms", smsProvider);
+ mTelephonyBackupAgent.putSmsMessagesToProvider(jsonReader);
+ assertEquals(mSmsRows.length, smsProvider.getRowsAdded());
+ assertEquals(mThreadProvider.mIsThreadArchived, mThreadProvider.mUpdateThreadsArchived);
+ }
+
+ /**
+ * Test restore mms with the empty json array "[]".
+ * @throws Exception
+ */
+ public void testRestoreMms_NoMms() throws Exception {
+ JsonReader jsonReader = new JsonReader(new StringReader(EMPTY_JSON_ARRAY));
+ FakeMmsProvider mmsProvider = new FakeMmsProvider(null);
+ mMockContentResolver.addProvider("mms", mmsProvider);
+ mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+ assertEquals(0, mmsProvider.getRowsAdded());
+ }
+
+ /**
+ * Test restore sms with three mms json object in the array.
+ * @throws Exception
+ */
+ public void testRestoreMms_AllMms() throws Exception {
+ JsonReader jsonReader = new JsonReader(new StringReader(addRandomDataToJson(mAllMmsJson)));
+ FakeMmsProvider mmsProvider = new FakeMmsProvider(mMmsAllContentValues);
+ mMockContentResolver.addProvider("mms", mmsProvider);
+ mTelephonyBackupAgent.putMmsMessagesToProvider(jsonReader);
+ assertEquals(18, mmsProvider.getRowsAdded());
+ assertEquals(mThreadProvider.mIsThreadArchived, mThreadProvider.mUpdateThreadsArchived);
+ }
+
+ /**
+ * 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
+ * of data. The agent does not take backup meta-info into consideration.
+ * @throws Exception
+ */
+ public void testBackup_WithQuotaExceeded() throws Exception {
+ mTelephonyBackupAgent.mMaxMsgPerFile = 1;
+ final int backupSize = 7168;
+ final int backupSizeAfterFirstQuotaHit = 6144;
+ final int backupSizeAfterSecondQuotaHit = 5120;
+
+ mSmsTable.addAll(Arrays.asList(mSmsRows));
+ mMmsTable.addAll(Arrays.asList(mMmsRows));
+
+ FullBackupDataOutput fullBackupDataOutput = new FullBackupDataOutput();
+ mTelephonyBackupAgent.onFullBackup(fullBackupDataOutput);
+ assertEquals(backupSize, fullBackupDataOutput.getSize());
+
+ mTelephonyBackupAgent.onQuotaExceeded(backupSize, backupSize - 100);
+ fullBackupDataOutput = new FullBackupDataOutput();
+ mTelephonyBackupAgent.onFullBackup(fullBackupDataOutput);
+ assertEquals(backupSizeAfterFirstQuotaHit, fullBackupDataOutput.getSize());
+
+ mTelephonyBackupAgent.onQuotaExceeded(backupSizeAfterFirstQuotaHit,
+ backupSizeAfterFirstQuotaHit - 200);
+ fullBackupDataOutput = new FullBackupDataOutput();
+ mTelephonyBackupAgent.onFullBackup(fullBackupDataOutput);
+ assertEquals(backupSizeAfterSecondQuotaHit, fullBackupDataOutput.getSize());
+ }
+
+ // Adding random keys to JSON to test handling it by the BackupAgent on restore.
+ private String addRandomDataToJson(String jsonString) throws JSONException {
+ JSONArray jsonArray = new JSONArray(jsonString);
+ JSONArray res = new JSONArray();
+ for (int i = 0; i < jsonArray.length(); ++i) {
+ JSONObject jsonObject = jsonArray.getJSONObject(i);
+ jsonObject.put(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+ res = res.put(jsonObject);
+ }
+ return res.toString();
+ }
+
+ /**
+ * class for checking sms insertion into the provider on restore.
+ */
+ private class FakeSmsProvider extends MockContentProvider {
+ private int nextRow = 0;
+ private ContentValues[] mSms;
+
+ public FakeSmsProvider(ContentValues[] sms) {
+ this.mSms = sms;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(Telephony.Sms.CONTENT_URI, uri);
+ ContentValues modifiedValues = new ContentValues(mSms[nextRow++]);
+ modifiedValues.remove(Telephony.Sms._ID);
+ modifiedValues.put(Telephony.Sms.READ, 1);
+ modifiedValues.put(Telephony.Sms.SEEN, 1);
+ if (mSubId2Phone.get(modifiedValues.getAsInteger(Telephony.Sms.SUBSCRIPTION_ID))
+ == null) {
+ modifiedValues.put(Telephony.Sms.SUBSCRIPTION_ID, -1);
+ }
+
+ if (modifiedValues.get(Telephony.Sms.ADDRESS) == null) {
+ modifiedValues.put(Telephony.Sms.ADDRESS, TelephonyBackupAgent.UNKNOWN_SENDER);
+ }
+
+ assertEquals(modifiedValues, values);
+ return null;
+ }
+
+ @Override
+ public int bulkInsert(Uri uri, ContentValues[] values) {
+ for (ContentValues cv : values) {
+ insert(uri, cv);
+ }
+ return values.length;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ public int getRowsAdded() {
+ return nextRow;
+ }
+ }
+
+ /**
+ * class for checking mms insertion into the provider on restore.
+ */
+ private class FakeMmsProvider extends MockContentProvider {
+ private int nextRow = 0;
+ private List<ContentValues> mValues;
+ private long mDummyMsgId = -1;
+ private long mMsgId = -1;
+
+ public FakeMmsProvider(List<ContentValues> values) {
+ this.mValues = values;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ Uri retUri = Uri.parse("dummy_uri");
+ ContentValues modifiedValues = new ContentValues(mValues.get(nextRow++));
+ if (APP_SMIL.equals(values.get(Telephony.Mms.Part.CONTENT_TYPE))) {
+ // Smil part.
+ assertEquals(-1, mDummyMsgId);
+ mDummyMsgId = values.getAsLong(Telephony.Mms.Part.MSG_ID);
+ }
+
+ if (values.get(Telephony.Mms.Part.SEQ) != null) {
+ // Part of mms.
+ final Uri expectedUri = Telephony.Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mDummyMsgId))
+ .appendPath("part")
+ .build();
+ assertEquals(expectedUri, uri);
+ }
+
+ if (values.get(Telephony.Mms.Part.MSG_ID) != null) {
+ modifiedValues.put(Telephony.Mms.Part.MSG_ID, mDummyMsgId);
+ }
+
+
+ if (values.get(Telephony.Mms.SUBSCRIPTION_ID) != null) {
+ assertEquals(Telephony.Mms.CONTENT_URI, uri);
+ if (mSubId2Phone.get(modifiedValues.getAsInteger(Telephony.Sms.SUBSCRIPTION_ID))
+ == null) {
+ modifiedValues.put(Telephony.Sms.SUBSCRIPTION_ID, -1);
+ }
+ // Mms.
+ modifiedValues.put(Telephony.Mms.READ, 1);
+ modifiedValues.put(Telephony.Mms.SEEN, 1);
+ mMsgId = modifiedValues.getAsInteger(BaseColumns._ID);
+ retUri = Uri.withAppendedPath(Telephony.Mms.CONTENT_URI, String.valueOf(mMsgId));
+ modifiedValues.remove(BaseColumns._ID);
+ }
+
+ if (values.get(Telephony.Mms.Addr.ADDRESS) != null) {
+ // Address.
+ final Uri expectedUri = Telephony.Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mMsgId))
+ .appendPath("addr")
+ .build();
+ assertEquals(expectedUri, uri);
+ assertNotSame(-1, mMsgId);
+ modifiedValues.put(Telephony.Mms.Addr.MSG_ID, mMsgId);
+ mDummyMsgId = -1;
+ }
+
+ for (String key : modifiedValues.keySet()) {
+ assertEquals("Key:"+key, modifiedValues.get(key), values.get(key));
+ }
+ assertEquals(modifiedValues.size(), values.size());
+ return retUri;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ final Uri expectedUri = Telephony.Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mDummyMsgId))
+ .appendPath("part")
+ .build();
+ assertEquals(expectedUri, uri);
+ ContentValues expected = new ContentValues();
+ expected.put(Telephony.Mms.Part.MSG_ID, mMsgId);
+ assertEquals(expected, values);
+ return 2;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ public int getRowsAdded() {
+ return nextRow;
+ }
+ }
+
+ /**
+ * class that implements MmsSms provider for thread ids.
+ */
+ private static class ThreadProvider extends MockContentProvider {
+ ArrayList<Set<Integer> > id2Thread = new ArrayList<>();
+ ArrayList<String> id2Recipient = new ArrayList<>();
+ Set<Integer> mIsThreadArchived = new HashSet<>();
+ Set<Integer> mUpdateThreadsArchived = new HashSet<>();
+
+
+ public int getOrCreateThreadId(final String[] recipients) {
+ if (recipients == null || recipients.length == 0) {
+ throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
+ }
+
+ Set<Integer> ids = new ArraySet<>();
+ for (String rec : recipients) {
+ if (!id2Recipient.contains(rec)) {
+ id2Recipient.add(rec);
+ }
+ ids.add(id2Recipient.indexOf(rec)+1);
+ }
+ if (!id2Thread.contains(ids)) {
+ id2Thread.add(ids);
+ }
+ return id2Thread.indexOf(ids)+1;
+ }
+
+ public void setArchived(int threadId) {
+ mIsThreadArchived.add(threadId);
+ }
+
+ private String getSpaceSepIds(int threadId) {
+ if (id2Thread.size() < threadId) {
+ return null;
+ }
+
+ String spaceSepIds = null;
+ for (Integer id : id2Thread.get(threadId-1)) {
+ spaceSepIds = (spaceSepIds == null ? "" : spaceSepIds + " ") + String.valueOf(id);
+ }
+ return spaceSepIds;
+ }
+
+ private String getRecipient(int recipientId) {
+ return id2Recipient.get(recipientId-1);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (uri.equals(TelephonyBackupAgent.ALL_THREADS_URI)) {
+ final int threadId = Integer.parseInt(selectionArgs[0]);
+ final String spaceSepIds = getSpaceSepIds(threadId);
+ List<ContentValues> table = new ArrayList<>();
+ ContentValues row = new ContentValues();
+ row.put(Telephony.Threads.RECIPIENT_IDS, spaceSepIds);
+ table.add(row);
+ return new FakeCursor(table, projection);
+ } else if (uri.toString().startsWith(Telephony.Threads.CONTENT_URI.toString())) {
+ assertEquals(1, projection.length);
+ assertEquals(Telephony.Threads.ARCHIVED, projection[0]);
+ List<String> segments = uri.getPathSegments();
+ final int threadId = Integer.parseInt(segments.get(segments.size() - 2));
+ List<ContentValues> table = new ArrayList<>();
+ ContentValues row = new ContentValues();
+ row.put(Telephony.Threads.ARCHIVED, mIsThreadArchived.contains(threadId) ? 1 : 0);
+ table.add(row);
+ return new FakeCursor(table, projection);
+ } else if (uri.toString().startsWith(
+ TelephonyBackupAgent.SINGLE_CANONICAL_ADDRESS_URI.toString())) {
+ final int recipientId = (int)ContentUris.parseId(uri);
+ final String recipient = getRecipient(recipientId);
+ List<ContentValues> table = new ArrayList<>();
+ ContentValues row = new ContentValues();
+ row.put(Telephony.CanonicalAddressesColumns.ADDRESS, recipient);
+ table.add(row);
+
+ return new FakeCursor(table,
+ projection != null
+ ? projection
+ : new String[] { Telephony.CanonicalAddressesColumns.ADDRESS });
+ } else if (uri.toString().startsWith(
+ TelephonyBackupAgent.THREAD_ID_CONTENT_URI.toString())) {
+ List<String> recipients = uri.getQueryParameters("recipient");
+
+ final int threadId =
+ getOrCreateThreadId(recipients.toArray(new String[recipients.size()]));
+ List<ContentValues> table = new ArrayList<>();
+ ContentValues row = new ContentValues();
+ row.put(BaseColumns._ID, String.valueOf(threadId));
+ table.add(row);
+ return new FakeCursor(table, projection);
+ } else {
+ fail("Unknown URI");
+ }
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ assertEquals(uri, Telephony.Threads.CONTENT_URI);
+ assertEquals(values.getAsInteger(Telephony.Threads.ARCHIVED).intValue(), 1);
+ final int threadId = Integer.parseInt(selectionArgs[0]);
+ mUpdateThreadsArchived.add(threadId);
+ return 1;
+ }
+ }
+
+ /**
+ * general cursor for serving queries.
+ */
+ private static class FakeCursor extends MockCursor {
+ String[] projection;
+ List<ContentValues> rows;
+ int nextRow = 0;
+
+ public FakeCursor(List<ContentValues> rows, String[] projection) {
+ this.projection = projection;
+ this.rows = rows;
+ }
+
+ public void setProjection(String[] projection) {
+ this.projection = projection;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return projection.length;
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ return projection[columnIndex];
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ return rows.get(nextRow).getAsString(projection[columnIndex]);
+ }
+
+ @Override
+ public int getInt(int columnIndex) {
+ return rows.get(nextRow).getAsInteger(projection[columnIndex]);
+ }
+
+ @Override
+ public long getLong(int columnIndex) {
+ return rows.get(nextRow).getAsLong(projection[columnIndex]);
+ }
+
+ @Override
+ public boolean isAfterLast() {
+ return nextRow >= getCount();
+ }
+
+ @Override
+ public boolean isLast() {
+ return nextRow == getCount() - 1;
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ nextRow = 0;
+ return getCount() > 0;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return getCount() > ++nextRow;
+ }
+
+ @Override
+ public int getCount() {
+ return rows.size();
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ for (int i=0; i<projection.length; ++i) {
+ if (columnName.equals(projection[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void close() {
+ }
+ }
+}