Merge "Support maximum wait time for Cell broadcast"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5769962..3b95bce 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />
@@ -66,6 +67,13 @@
android:singleUser="true"
android:readPermission="android.permission.READ_SMS" />
+ <provider android:name="SmsChangesProvider"
+ android:authorities="sms-changes"
+ android:multiprocess="false"
+ android:exported="true"
+ android:singleUser="true"
+ android:readPermission="android.permission.READ_SMS" />
+
<!-- This is a singleton provider that is used by all users.
A new instance is not created for each user. And the db is shared
as well.
diff --git a/assets/carrier_list.pb b/assets/carrier_list.pb
index 011ba01..125de1a 100644
--- a/assets/carrier_list.pb
+++ b/assets/carrier_list.pb
Binary files differ
diff --git a/assets/carrier_list.textpb b/assets/carrier_list.textpb
index d94a50a..648571e 100644
--- a/assets/carrier_list.textpb
+++ b/assets/carrier_list.textpb
Binary files differ
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 1c53b15..309a57b 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" product="tablet" msgid="9194799012395299737">"تهيئة شبكة الجوال"</string>
- <string name="app_label" product="default" msgid="8338087656149558019">"سعة تخزينية للهاتف والرسائل"</string>
+ <string name="app_label" product="default" msgid="8338087656149558019">"مساحة تخزين للهاتف والرسائل"</string>
</resources>
diff --git a/res/values-as/config.xml b/res/values-as/config.xml
new file mode 100644
index 0000000..99877a6
--- /dev/null
+++ b/res/values-as/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="persist_apns_for_plmn">
+ <item msgid="6413072509259000954">"20404"</item>
+ <item msgid="5639159280778239123">"310004"</item>
+ <item msgid="3860605521380788028">"310120"</item>
+ <item msgid="537693705785480198">"311480"</item>
+ </string-array>
+</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
new file mode 100644
index 0000000..6994d18
--- /dev/null
+++ b/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2008 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" product="tablet" msgid="9194799012395299737">"ম’বাইল নেটৱৰ্ক কনফিগাৰেশ্বন"</string>
+ <string name="app_label" product="default" msgid="8338087656149558019">"ফ\'ন আৰু বাৰ্তাৰ সঞ্চয়াগাৰ"</string>
+</resources>
diff --git a/res/values-or/config.xml b/res/values-or/config.xml
new file mode 100644
index 0000000..99877a6
--- /dev/null
+++ b/res/values-or/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="persist_apns_for_plmn">
+ <item msgid="6413072509259000954">"20404"</item>
+ <item msgid="5639159280778239123">"310004"</item>
+ <item msgid="3860605521380788028">"310120"</item>
+ <item msgid="537693705785480198">"311480"</item>
+ </string-array>
+</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
new file mode 100644
index 0000000..b004c4f
--- /dev/null
+++ b/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2008 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" product="tablet" msgid="9194799012395299737">"ମୋବାଇଲ୍ ନେଟ୍ୱର୍କ କନଫିଗରେଶନ୍"</string>
+ <string name="app_label" product="default" msgid="8338087656149558019">"ଫୋନ୍ ଓ ମେସେଜିଙ୍ଗ ଷ୍ଟୋରେଜ୍"</string>
+</resources>
diff --git a/src/com/android/providers/telephony/CarrierIdProvider.java b/src/com/android/providers/telephony/CarrierIdProvider.java
index 237a197..44f053d 100644
--- a/src/com/android/providers/telephony/CarrierIdProvider.java
+++ b/src/com/android/providers/telephony/CarrierIdProvider.java
@@ -332,6 +332,7 @@
*/
public CarrierIdDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ Log.d(TAG, "CarrierIdDatabaseHelper: " + DATABASE_VERSION);
setWriteAheadLoggingEnabled(false);
}
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 9f98732..4add85d 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.DefaultDatabaseErrorHandler;
@@ -293,12 +294,12 @@
mContext = context;
// Memory optimization - close idle connections after 30s of inactivity
setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+ setWriteAheadLoggingEnabled(false);
try {
PhoneFactory.addLocalLog(TAG, 100);
} catch (IllegalArgumentException e) {
// ignore
}
- setWriteAheadLoggingEnabled(false);
}
private static synchronized MmsSmsDatabaseErrorHandler getDbErrorHandler(Context context) {
@@ -1069,6 +1070,29 @@
Sms.TYPE + "=" + Sms.MESSAGE_TYPE_INBOX +
" OR " +
Sms.TYPE + "=" + Sms.MESSAGE_TYPE_SENT + ";");
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ // Create a table to keep track of changes to SMS table - specifically on update to read
+ // and deletion of msgs
+ db.execSQL("CREATE TABLE sms_changes (" +
+ "_id INTEGER PRIMARY KEY," +
+ "orig_rowid INTEGER," +
+ "sub_id INTEGER," +
+ "type INTEGER," +
+ "new_read_status INTEGER" +
+ ");");
+ db.execSQL("CREATE TRIGGER sms_update_on_read_change_row " +
+ "AFTER UPDATE OF read ON sms WHEN NEW.read != OLD.read " +
+ "BEGIN " +
+ " INSERT INTO sms_changes VALUES(null, NEW._id, NEW.sub_id, " +
+ "0, NEW.read); " +
+ "END;");
+ db.execSQL("CREATE TRIGGER sms_delete_change_row " +
+ "AFTER DELETE ON sms " +
+ "BEGIN " +
+ " INSERT INTO sms_changes values(null, OLD._id, OLD.sub_id, 1, null); " +
+ "END;");
+ }
}
@VisibleForTesting
diff --git a/src/com/android/providers/telephony/SmsChangesProvider.java b/src/com/android/providers/telephony/SmsChangesProvider.java
new file mode 100644
index 0000000..3f14e2b
--- /dev/null
+++ b/src/com/android/providers/telephony/SmsChangesProvider.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 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.app.AppOpsManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * This is the ContentProvider for the table sms_changes.
+ * This provider is applicable only for Android Auto builds as
+ * this table exists only in Android Auto environment.
+ *
+ * This provider does not notify of changes.
+ * Interested observers should instead listen to notification on sms table, instead.
+ */
+public class SmsChangesProvider extends ContentProvider {
+ private final static String TAG = "SmsChangesProvider";
+
+ private static final String TABLE_SMS_CHANGES = "sms_changes";
+
+ // Db open helper for tables stored in CE(Credential Encrypted) storage.
+ private SQLiteOpenHelper mCeOpenHelper;
+
+ @Override
+ public String getType(Uri url) {
+ return null;
+ }
+
+ @Override
+ public boolean onCreate() {
+ setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
+ mCeOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
+ return true;
+ }
+
+
+ @Override
+ public Cursor query(Uri url, String[] projectionIn, String selection,
+ String[] selectionArgs, String sort) {
+ // return if FEATURE_AUTOMOTIVE is not set
+ if (!isAutoFeatureSet()) {
+ return null;
+ }
+
+ // Only support one type of query
+ // Caller sends content://mms-sms and other params
+ if (!isUrlSupported(url)) {
+ return null;
+ }
+
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_SMS_CHANGES);
+ SQLiteDatabase db = mCeOpenHelper.getReadableDatabase();
+ return qb.query(db, projectionIn, selection, selectionArgs,
+ null /* groupBy */, null /* having */,
+ null /* sortOrder */);
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ // return if FEATURE_AUTOMOTIVE is not set
+ if (!isAutoFeatureSet()) {
+ return 0;
+ }
+
+ // only support deletion of all data from the table
+ if (!isUrlSupported(url)) return 0;
+
+ return mCeOpenHelper.getWritableDatabase().delete(TABLE_SMS_CHANGES,
+ null /* whereClause */, null /* whereArgs */);
+ }
+
+ private boolean isUrlSupported(Uri url) {
+ if (sURLMatcher.match(url) != SMSCHANGES_URL) {
+ Log.e(TAG, "Invalid or Unsupported request: " + url);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ return 0;
+ }
+
+ private boolean isAutoFeatureSet() {
+ return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ private static final int SMSCHANGES_URL = 0;
+
+ private static final UriMatcher sURLMatcher =
+ new UriMatcher(UriMatcher.NO_MATCH);
+
+ static {
+ sURLMatcher.addURI("sms-changes", null, SMSCHANGES_URL);
+ }
+}
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index ad4367c..437b5a2 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/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 24ff035..6629eb9 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -138,6 +138,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.zip.CRC32;
public class TelephonyProvider extends ContentProvider
@@ -2779,7 +2780,7 @@
List<String> constraints = new ArrayList<String>();
int match = s_urlMatcher.match(url);
- checkQueryPermission(match, projectionIn);
+ checkQueryPermission(match, projectionIn, selection);
switch (match) {
case URL_TELEPHONY_USING_SUBID: {
subIdString = url.getLastPathSegment();
@@ -2963,26 +2964,6 @@
qb.appendWhere(TextUtils.join(" AND ", constraints));
}
- if (match != URL_SIMINFO) {
- // Determine if we need to do a check for fields in the selection
- boolean selectionContainsSensitiveFields;
- try {
- selectionContainsSensitiveFields = containsSensitiveFields(selection);
- } catch (Exception e) {
- // Malformed sql, check permission anyway.
- selectionContainsSensitiveFields = true;
- }
-
- if (selectionContainsSensitiveFields) {
- try {
- checkPermission();
- } catch (SecurityException e) {
- EventLog.writeEvent(0x534e4554, "124107808", Binder.getCallingUid());
- throw e;
- }
- }
- }
-
SQLiteDatabase db = getReadableDatabase();
Cursor ret = null;
try {
@@ -3008,8 +2989,27 @@
return ret;
}
- private void checkQueryPermission(int match, String[] projectionIn) {
- if (match != URL_SIMINFO) {
+ private void checkQueryPermission(int match, String[] projectionIn, String selection) {
+ if (match != URL_SIMINFO && match != URL_SIMINFO_USING_SUBID) {
+ // Determine if we need to do a check for fields in the selection
+ boolean selectionContainsSensitiveFields;
+ try {
+ selectionContainsSensitiveFields = containsSensitiveFields(selection);
+ } catch (IllegalArgumentException e) {
+ // Malformed sql, check permission anyway and return.
+ checkPermission();
+ return;
+ }
+
+ if (selectionContainsSensitiveFields) {
+ try {
+ checkPermission();
+ } catch (SecurityException e) {
+ EventLog.writeEvent(0x534e4554, "124107808", Binder.getCallingUid());
+ throw e;
+ }
+ }
+
if (projectionIn != null) {
for (String column : projectionIn) {
if (TYPE.equals(column) ||
@@ -3019,7 +3019,6 @@
MVNO_TYPE.equals(column) ||
MVNO_MATCH_DATA.equals(column) ||
APN.equals(column)) {
- // noop
} else {
checkPermission();
break;
@@ -3029,9 +3028,27 @@
// null returns all columns, so need permission check
checkPermission();
}
+ } else {
+ // if querying siminfo, caller should have read privilege permissions
+ checkPhonePrivilegePermission();
}
}
+ private boolean containsSensitiveFields(String sqlStatement) {
+ try {
+ SqlTokenFinder.findTokens(sqlStatement, s -> {
+ switch (s) {
+ case USER:
+ case PASSWORD:
+ throw new SecurityException();
+ }
+ });
+ } catch (SecurityException e) {
+ return true;
+ }
+ return false;
+ }
+
/**
* To find the current sim APN. Query APN based on {MCC, MNC, MVNO} to support backward
* compatibility but will move to carrier id in the future.
@@ -3117,21 +3134,6 @@
}
}
- private boolean containsSensitiveFields(String sqlStatement) {
- try {
- SqlTokenFinder.findTokens(sqlStatement, s -> {
- switch (s) {
- case USER:
- case PASSWORD:
- throw new SecurityException();
- }
- });
- } catch (SecurityException e) {
- return true;
- }
- return false;
- }
-
@Override
public String getType(Uri url)
{
@@ -3815,6 +3817,15 @@
throw new SecurityException("No permission to write APN settings");
}
+ private void checkPhonePrivilegePermission() {
+ int status = getContext().checkCallingOrSelfPermission(
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ if (status == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException("No phone privilege permission");
+ }
+
private DatabaseHelper mOpenHelper;
private void restoreDefaultAPN(int subId) {
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