18b39acf181c547e87161873112d6c52a581fc778Roman Sorokin/*
28b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * Copyright (C) 2016 The Android Open Source Project
38b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
48b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * Licensed under the Apache License, Version 2.0 (the "License");
58b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * you may not use this file except in compliance with the License.
68b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * You may obtain a copy of the License at
78b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
88b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *      http://www.apache.org/licenses/LICENSE-2.0
98b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * Unless required by applicable law or agreed to in writing, software
118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * distributed under the License is distributed on an "AS IS" BASIS,
128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * See the License for the specific language governing permissions and
148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * limitations under the License
158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin */
168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
178b39acf181c547e87161873112d6c52a581fc778Roman Sorokinpackage com.android.providers.telephony;
188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
198b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport com.google.android.mms.ContentType;
208b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport com.google.android.mms.pdu.CharacterSets;
218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
228b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport com.android.internal.annotations.VisibleForTesting;
238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
248b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.annotation.TargetApi;
2521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport android.app.AlarmManager;
2621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport android.app.IntentService;
278b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.app.backup.BackupAgent;
288b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.app.backup.BackupDataInput;
298b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.app.backup.BackupDataOutput;
308b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.app.backup.FullBackupDataOutput;
319037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokinimport android.content.ContentResolver;
328b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.content.ContentUris;
338b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.content.ContentValues;
3421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport android.content.Context;
3521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport android.content.Intent;
3621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport android.content.SharedPreferences;
378b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.database.Cursor;
388b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.database.DatabaseUtils;
398b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.net.Uri;
408b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.os.Build;
418b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.os.ParcelFileDescriptor;
42c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokinimport android.os.PowerManager;
438b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.provider.BaseColumns;
448b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.provider.Telephony;
458b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.telephony.PhoneNumberUtils;
468b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.telephony.SubscriptionInfo;
478b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.telephony.SubscriptionManager;
48a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokinimport android.text.TextUtils;
498b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.ArrayMap;
508b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.ArraySet;
518b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.JsonReader;
528b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.JsonWriter;
538b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.Log;
548b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport android.util.SparseArray;
558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
5621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.io.BufferedWriter;
578b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.File;
588b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.FileDescriptor;
5921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.io.FileFilter;
608b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.FileInputStream;
618b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.IOException;
628b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.InputStreamReader;
638b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.io.OutputStreamWriter;
648b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.ArrayList;
6521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.util.Arrays;
6621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.util.Comparator;
6721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.util.HashMap;
688b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.List;
698b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.Locale;
708b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.Map;
718b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.Set;
7221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokinimport java.util.concurrent.TimeUnit;
738b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.zip.DeflaterOutputStream;
748b39acf181c547e87161873112d6c52a581fc778Roman Sorokinimport java.util.zip.InflaterInputStream;
758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin/***
778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * Backup agent for backup and restore SMS's and text MMS's.
788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * This backup agent stores SMS's into "sms_backup" file as a JSON array. Example below.
808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  [{"self_phone":"+1234567891011","address":"+1234567891012","body":"Example sms",
818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "date":"1450893518140","date_sent":"1450893514000","status":"-1","type":"1"},
828b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  {"self_phone":"+1234567891011","address":"12345","body":"Example 2","date":"1451328022316",
838b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "date_sent":"1451328018000","status":"-1","type":"1"}]
848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
858b39acf181c547e87161873112d6c52a581fc778Roman Sorokin * Text MMS's are stored into "mms_backup" file as a JSON array. Example below.
868b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  [{"self_phone":"+1234567891011","date":"1451322716","date_sent":"0","m_type":"128","v":"18",
878b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "msg_box":"2","mms_addresses":[{"type":137,"address":"+1234567891011","charset":106},
888b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  {"type":151,"address":"example@example.com","charset":106}],"mms_body":"Mms to email",
898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "mms_charset":106},
908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  {"self_phone":"+1234567891011","sub":"MMS subject","date":"1451322955","date_sent":"0",
918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "m_type":"132","v":"17","msg_box":"1","ct_l":"http://promms/servlets/NOK5BBqgUHAqugrQNM",
928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "mms_addresses":[{"type":151,"address":"+1234567891011","charset":106}],
938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "mms_body":"Mms\nBody\r\n",
948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *  "mms_charset":106,"sub_cs":"106"}]
958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *
968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *   It deflates the files on the flight.
978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin *   Every 1000 messages it backs up file, deletes it and creates a new one with the same name.
988f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin *
998f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin *   It stores how many bytes we are over the quota and don't backup the oldest messages.
1008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin */
1018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin@TargetApi(Build.VERSION_CODES.M)
1038b39acf181c547e87161873112d6c52a581fc778Roman Sorokinpublic class TelephonyBackupAgent extends BackupAgent {
1048b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String TAG = "TelephonyBackupAgent";
1058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final boolean DEBUG = false;
1068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
1098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final int DEFAULT_DURATION = 5000; //ms
1108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
1128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
1138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String sSmilTextOnly =
1148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            "<smil>" +
1158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                "<head>" +
1168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    "<layout>" +
1178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        "<root-layout/>" +
1188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        "<region id=\"Text\" top=\"0\" left=\"0\" "
1198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        + "height=\"100%%\" width=\"100%%\"/>" +
1208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    "</layout>" +
1218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                "</head>" +
1228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                "<body>" +
1238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                       "%s" +  // constructed body goes here
1248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                "</body>" +
1258b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            "</smil>";
1268b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1278b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
1288b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
1298b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String sSmilTextPart =
1308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            "<par dur=\"" + DEFAULT_DURATION + "ms\">" +
1318b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                "<text src=\"%s\" region=\"Text\" />" +
1328b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            "</par>";
1338b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1348b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // JSON key for phone number a message was sent from or received to.
1368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String SELF_PHONE_KEY = "self_phone";
1378b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // JSON key for list of addresses of MMS message.
1388b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String MMS_ADDRESSES_KEY = "mms_addresses";
13976422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin    // JSON key for list of recipients of the message.
14076422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin    private static final String RECIPIENTS = "recipients";
1418b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // JSON key for MMS body.
1428b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String MMS_BODY_KEY = "mms_body";
1438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // JSON key for MMS charset.
1448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String MMS_BODY_CHARSET_KEY = "mms_charset";
1458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
14621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // File names suffixes for backup/restore.
14721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String SMS_BACKUP_FILE_SUFFIX = "_sms_backup";
14821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String MMS_BACKUP_FILE_SUFFIX = "_mms_backup";
14921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
15021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // File name formats for backup. It looks like 000000_sms_backup, 000001_sms_backup, etc.
15121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String SMS_BACKUP_FILE_FORMAT = "%06d"+SMS_BACKUP_FILE_SUFFIX;
15221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String MMS_BACKUP_FILE_FORMAT = "%06d"+MMS_BACKUP_FILE_SUFFIX;
1538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Charset being used for reading/writing backup files.
1558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String CHARSET_UTF8 = "UTF-8";
1568b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1578b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Order by ID entries from database.
1588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final String ORDER_BY_ID = BaseColumns._ID + " ASC";
1598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
16021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Order by Date entries from database. We start backup from the oldest.
1618f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin    private static final String ORDER_BY_DATE = "date ASC";
1628f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
16356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    // This is a hard coded string rather than a localized one because we don't want it to
16456117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    // change when you change locale.
16556117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    @VisibleForTesting
16656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    static final String UNKNOWN_SENDER = "\u02BCUNKNOWN_SENDER!\u02BC";
16756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
16856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    // Thread id for UNKNOWN_SENDER.
16956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    private long mUnknownSenderThreadId;
17056117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
1718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Columns from SMS database for backup/restore.
1728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
1738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String[] SMS_PROJECTION = new String[] {
1748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms._ID,
1758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.SUBSCRIPTION_ID,
1768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.ADDRESS,
1778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.BODY,
1788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.SUBJECT,
1798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.DATE,
1808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.DATE_SENT,
1818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Sms.STATUS,
182a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Telephony.Sms.TYPE,
183a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Telephony.Sms.THREAD_ID
184a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    };
185a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
186a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    // Columns to fetch recepients of SMS.
187a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    private static final String[] SMS_RECIPIENTS_PROJECTION = {
188a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Telephony.Threads._ID,
189a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Telephony.Threads.RECIPIENT_IDS
1908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    };
1918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
1928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Columns from MMS database for backup/restore.
1938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
1948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String[] MMS_PROJECTION = new String[] {
1958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms._ID,
1968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.SUBSCRIPTION_ID,
1978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.SUBJECT,
1988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.SUBJECT_CHARSET,
1998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.DATE,
2008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.DATE_SENT,
2018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.MESSAGE_TYPE,
2028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.MMS_VERSION,
2038b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.MESSAGE_BOX,
20476422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin            Telephony.Mms.CONTENT_LOCATION,
2053aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            Telephony.Mms.THREAD_ID,
2063aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            Telephony.Mms.TRANSACTION_ID
2078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    };
2088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Columns from addr database for backup/restore. This database is used for fetching addresses
2108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // for MMS message.
2118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
2128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String[] MMS_ADDR_PROJECTION = new String[] {
2138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.Addr.TYPE,
2148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.Addr.ADDRESS,
2158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.Addr.CHARSET
2168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    };
2178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Columns from part database for backup/restore. This database is used for fetching body text
2198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // and charset for MMS message.
2208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
2218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String[] MMS_TEXT_PROJECTION = new String[] {
2228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.Part.TEXT,
2238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Telephony.Mms.Part.CHARSET
2248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    };
22521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    static final int MMS_TEXT_IDX = 0;
22621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    static final int MMS_TEXT_CHARSET_IDX = 1;
22721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
22821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Buffer size for Json writer.
22921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    public static final int WRITER_BUFFER_SIZE = 32*1024; //32Kb
23021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
23121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // We increase how many bytes backup size over quota by 10%, so we will fit into quota on next
23221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // backup
23321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    public static final double BYTES_OVER_QUOTA_MULTIPLIER = 1.1;
2348b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Maximum messages for one backup file. After reaching the limit the agent backs up the file,
2368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // deletes it and creates a new one with the same name.
2379037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    // Not final for the testing.
23821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @VisibleForTesting
2399037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    int mMaxMsgPerFile = 1000;
2408b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2418b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    // Default values for SMS, MMS, Addresses restore.
24256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    private static ContentValues sDefaultValuesSms = new ContentValues(5);
24356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    private static ContentValues sDefaultValuesMms = new ContentValues(6);
24421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final ContentValues sDefaultValuesAddr = new ContentValues(2);
2458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2468f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin    // Shared preferences for the backup agent.
2478f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin    private static final String BACKUP_PREFS = "backup_shared_prefs";
24821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Key for storing quota bytes.
24921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String QUOTA_BYTES = "backup_quota_bytes";
25021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Key for storing backup data size.
25121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String BACKUP_DATA_BYTES = "backup_data_bytes";
25221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Key for storing timestamp when backup agent resets quota. It does that to get onQuotaExceeded
25321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // call so it could get the new quota if it changed.
25421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final String QUOTA_RESET_TIME = "reset_quota_time";
25521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final long QUOTA_RESET_INTERVAL = 30 * AlarmManager.INTERVAL_DAY; // 30 days.
2568f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
2578f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
2588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static {
2598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Consider restored messages read and seen.
26021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesSms.put(Telephony.Sms.READ, 1);
26121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesSms.put(Telephony.Sms.SEEN, 1);
26256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        sDefaultValuesSms.put(Telephony.Sms.ADDRESS, UNKNOWN_SENDER);
2638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // If there is no sub_id with self phone number on restore set it to -1.
26421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesSms.put(Telephony.Sms.SUBSCRIPTION_ID, -1);
2658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
26621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.READ, 1);
26721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.SEEN, 1);
26821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.SUBSCRIPTION_ID, -1);
26921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_ALL);
27021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.TEXT_ONLY, 1);
2718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
27221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesAddr.put(Telephony.Mms.Addr.TYPE, 0);
27321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sDefaultValuesAddr.put(Telephony.Mms.Addr.CHARSET, CharacterSets.DEFAULT_CHARSET);
2748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
2758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2779037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private SparseArray<String> mSubId2phone = new SparseArray<String>();
2789037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private Map<String, Integer> mPhone2subId = new ArrayMap<String, Integer>();
2793aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private Map<Long, Boolean> mThreadArchived = new HashMap<>();
2808f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
2819037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private ContentResolver mContentResolver;
28221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // How many bytes we can backup to fit into quota.
2838f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin    private long mBytesOverQuota;
2848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
28521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Cache list of recipients by threadId. It reduces db requests heavily. Used during backup.
28621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @VisibleForTesting
2879037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    Map<Long, List<String>> mCacheRecipientsByThread = null;
28821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Cache threadId by list of recipients. Used during restore.
28921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @VisibleForTesting
2909037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    Map<Set<String>, Long> mCacheGetOrCreateThreadId = null;
29121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
2928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @Override
2938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    public void onCreate() {
2948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        super.onCreate();
2958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
2968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final SubscriptionManager subscriptionManager = SubscriptionManager.from(this);
2978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (subscriptionManager != null) {
2988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final List<SubscriptionInfo> subInfo =
2998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    subscriptionManager.getActiveSubscriptionInfoList();
3008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (subInfo != null) {
3018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                for (SubscriptionInfo sub : subInfo) {
3028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    final String phoneNumber = getNormalizedNumber(sub);
3039037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    mSubId2phone.append(sub.getSubscriptionId(), phoneNumber);
3049037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    mPhone2subId.put(phoneNumber, sub.getSubscriptionId());
3058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
3068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
3078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
3089037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        mContentResolver = getContentResolver();
30956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        initUnknownSender();
31021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
31121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
31221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @VisibleForTesting
3139037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    void setContentResolver(ContentResolver contentResolver) {
3149037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        mContentResolver = contentResolver;
3159037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    }
3169037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    @VisibleForTesting
3179037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    void setSubId(SparseArray<String> subId2Phone, Map<String, Integer> phone2subId) {
3189037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        mSubId2phone = subId2Phone;
3199037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        mPhone2subId = phone2subId;
3208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
3218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
32256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    @VisibleForTesting
32356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    void initUnknownSender() {
32456117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        mUnknownSenderThreadId = getOrCreateThreadId(null);
32556117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        sDefaultValuesSms.put(Telephony.Sms.THREAD_ID, mUnknownSenderThreadId);
32656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        sDefaultValuesMms.put(Telephony.Mms.THREAD_ID, mUnknownSenderThreadId);
32756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin    }
32856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
3298b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @Override
3308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    public void onFullBackup(FullBackupDataOutput data) throws IOException {
33121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        SharedPreferences sharedPreferences = getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE);
33221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        if (sharedPreferences.getLong(QUOTA_RESET_TIME, Long.MAX_VALUE) <
33321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                System.currentTimeMillis()) {
33421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            clearSharedPreferences();
33521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
3368f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
33721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        mBytesOverQuota = sharedPreferences.getLong(BACKUP_DATA_BYTES, 0) -
33821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                sharedPreferences.getLong(QUOTA_BYTES, Long.MAX_VALUE);
33921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        if (mBytesOverQuota > 0) {
34021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            mBytesOverQuota *= BYTES_OVER_QUOTA_MULTIPLIER;
34121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
3428f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
34321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        try (
3449037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                Cursor smsCursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, SMS_PROJECTION,
34521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                        null, null, ORDER_BY_DATE);
34621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                // Do not backup non text-only MMS's.
3479037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                Cursor mmsCursor = mContentResolver.query(Telephony.Mms.CONTENT_URI, MMS_PROJECTION,
34821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                        Telephony.Mms.TEXT_ONLY+"=1", null, ORDER_BY_DATE)) {
3498f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin
35021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (smsCursor != null) {
35121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                smsCursor.moveToFirst();
3528b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
35321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (mmsCursor != null) {
35421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                mmsCursor.moveToFirst();
35521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
35621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
35721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            // It backs up messages from the oldest to newest. First it looks at the timestamp of
35821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            // the next SMS messages and MMS message. If the SMS is older it backs up 1000 SMS
35921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            // messages, otherwise 1000 MMS messages. Repeat until out of SMS's or MMS's.
36021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            // It ensures backups are incremental.
36121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            int fileNum = 0;
36221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            while (smsCursor != null && !smsCursor.isAfterLast() &&
36321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    mmsCursor != null && !mmsCursor.isAfterLast()) {
36421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                final long smsDate = TimeUnit.MILLISECONDS.toSeconds(getMessageDate(smsCursor));
36521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                final long mmsDate = getMessageDate(mmsCursor);
36621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                if (smsDate < mmsDate) {
36756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                    backupAll(data, smsCursor,
36856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                            String.format(Locale.US, SMS_BACKUP_FILE_FORMAT, fileNum++));
36921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                } else {
37056117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                    backupAll(data, mmsCursor, String.format(Locale.US,
37156117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                            MMS_BACKUP_FILE_FORMAT, fileNum++));
3728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
3738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
37421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
37521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            while (smsCursor != null && !smsCursor.isAfterLast()) {
37656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                backupAll(data, smsCursor,
37756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                        String.format(Locale.US, SMS_BACKUP_FILE_FORMAT, fileNum++));
37821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
37921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
38021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            while (mmsCursor != null && !mmsCursor.isAfterLast()) {
38156117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                backupAll(data, mmsCursor,
38256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                        String.format(Locale.US, MMS_BACKUP_FILE_FORMAT, fileNum++));
38321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
3848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
3853aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
3863aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        mThreadArchived = new HashMap<>();
3878f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin    }
3888b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
38921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @VisibleForTesting
39021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    void clearSharedPreferences() {
39121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE).edit()
39221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .remove(BACKUP_DATA_BYTES)
39321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .remove(QUOTA_BYTES)
39421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .remove(QUOTA_RESET_TIME)
39521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .apply();
39621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
39721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
39821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static long getMessageDate(Cursor cursor) {
39921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        return cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE));
40021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
40121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
40221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @Override
40321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
40421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        SharedPreferences sharedPreferences = getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE);
40521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        if (sharedPreferences.contains(BACKUP_DATA_BYTES)
40621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                && sharedPreferences.contains(QUOTA_BYTES)) {
40721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            // Increase backup size by the size we skipped during previous backup.
40821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            backupDataBytes += (sharedPreferences.getLong(BACKUP_DATA_BYTES, 0)
40921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    - sharedPreferences.getLong(QUOTA_BYTES, 0)) * BYTES_OVER_QUOTA_MULTIPLIER;
41021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
41121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        sharedPreferences.edit()
41221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .putLong(BACKUP_DATA_BYTES, backupDataBytes)
41321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .putLong(QUOTA_BYTES, quotaBytes)
41421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .putLong(QUOTA_RESET_TIME, System.currentTimeMillis() + QUOTA_RESET_INTERVAL)
41521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                .apply();
41621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
41721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
41821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private void backupAll(FullBackupDataOutput data, Cursor cursor, String fileName)
41921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            throws IOException {
42021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        if (cursor == null || cursor.isAfterLast()) {
42121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            return;
42221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
42321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
42421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        int messagesWritten = 0;
42521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        try (JsonWriter jsonWriter = getJsonWriter(fileName)) {
42621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (fileName.endsWith(SMS_BACKUP_FILE_SUFFIX)) {
4279037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                messagesWritten = putSmsMessagesToJson(cursor, jsonWriter);
42821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            } else {
4299037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                messagesWritten = putMmsMessagesToJson(cursor, jsonWriter);
4308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
4318b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
43221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        backupFile(messagesWritten, fileName, data);
4338b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
4348b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
4358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
4369037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    int putMmsMessagesToJson(Cursor cursor,
4379037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                             JsonWriter jsonWriter) throws IOException {
4388b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.beginArray();
43921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        int msgCount;
4409037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        for (msgCount = 0; msgCount < mMaxMsgPerFile && !cursor.isAfterLast();
4419037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                cursor.moveToNext()) {
4429037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            msgCount += writeMmsToWriter(jsonWriter, cursor);
4438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
4448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.endArray();
44521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        return msgCount;
4468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
4478b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
4488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
4499037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    int putSmsMessagesToJson(Cursor cursor, JsonWriter jsonWriter) throws IOException {
4508b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
4518b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.beginArray();
45221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        int msgCount;
4539037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        for (msgCount = 0; msgCount < mMaxMsgPerFile && !cursor.isAfterLast();
45421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                ++msgCount, cursor.moveToNext()) {
4559037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            writeSmsToWriter(jsonWriter, cursor);
4568b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
4578b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.endArray();
45821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        return msgCount;
4598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
4608b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
46121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private void backupFile(int messagesWritten, String fileName, FullBackupDataOutput data)
46221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            throws IOException {
4638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final File file = new File(getFilesDir().getPath() + "/" + fileName);
4648f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin        try {
46521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (messagesWritten > 0) {
46621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                if (mBytesOverQuota > 0) {
46721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    mBytesOverQuota -= file.length();
46821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    return;
46921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                }
47021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                super.fullBackupFile(file, data);
4718f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin            }
4728f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin        } finally {
4738f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin            file.delete();
4748f63351b0fb1598b6add38bc3c20148b7c65e731Roman Sorokin        }
4758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
4768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
47721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    public static class DeferredSmsMmsRestoreService extends IntentService {
47821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        private static final String TAG = "DeferredSmsMmsRestoreService";
4798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
48021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        private final Comparator<File> mFileComparator = new Comparator<File>() {
48121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            @Override
48221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            public int compare(File lhs, File rhs) {
48321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                return rhs.getName().compareTo(lhs.getName());
4848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
48521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        };
48621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
48721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        public DeferredSmsMmsRestoreService() {
48821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            super(TAG);
48921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            setIntentRedelivery(true);
49021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
49121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
4929037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        private TelephonyBackupAgent mTelephonyBackupAgent;
493c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin        private PowerManager.WakeLock mWakeLock;
4949037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin
49521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        @Override
49621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        protected void onHandleIntent(Intent intent) {
497c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin            try {
498c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                mWakeLock.acquire();
4992d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                File[] files = getFilesToRestore(this);
50021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
5012d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                if (files == null || files.length == 0) {
502c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                    return;
503c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                }
504c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                Arrays.sort(files, mFileComparator);
505c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin
506c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                for (File file : files) {
507c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                    final String fileName = file.getName();
508c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                    try (FileInputStream fileInputStream = new FileInputStream(file)) {
509c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                        mTelephonyBackupAgent.doRestoreFile(fileName, fileInputStream.getFD());
51056117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                    } catch (Exception e) {
51156117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                        // Either IOException or RuntimeException.
51256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                        Log.e(TAG, e.toString());
51356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                    } finally {
514c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                        file.delete();
51521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    }
51621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                }
517c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin            } finally {
518c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin                mWakeLock.release();
5198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
5208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
52121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
52221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        @Override
52321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        public void onCreate() {
52421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            super.onCreate();
5259037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mTelephonyBackupAgent = new TelephonyBackupAgent();
5269037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mTelephonyBackupAgent.attach(this);
5279037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mTelephonyBackupAgent.onCreate();
528c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin
529c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
530c2231c02010a2a5915e4d09ed5fbabd0bba65078Roman Sorokin            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
5319037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        }
53221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
5339037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        @Override
5349037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        public void onDestroy() {
5359037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            if (mTelephonyBackupAgent != null) {
5369037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mTelephonyBackupAgent.onDestroy();
5379037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mTelephonyBackupAgent = null;
5389037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            }
5399037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            super.onDestroy();
54021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
54121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
5422d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin        static void startIfFilesExist(Context context) {
5432d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            File[] files = getFilesToRestore(context);
5442d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            if (files == null || files.length == 0) {
5452d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                return;
5462d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            }
5472d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            context.startService(new Intent(context, DeferredSmsMmsRestoreService.class));
5482d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin        }
5492d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin
5502d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin        private static File[] getFilesToRestore(Context context) {
5512d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            return context.getFilesDir().listFiles(new FileFilter() {
5522d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                @Override
5532d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                public boolean accept(File file) {
5542d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                    return file.getName().endsWith(SMS_BACKUP_FILE_SUFFIX) ||
5552d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                            file.getName().endsWith(MMS_BACKUP_FILE_SUFFIX);
5562d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin                }
5572d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin            });
55821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
55921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
56021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
56121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    @Override
56221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    public void onRestoreFinished() {
56321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        super.onRestoreFinished();
5642d3a779b41d5a553c8615a906ee97da29907313eRoman Sorokin        DeferredSmsMmsRestoreService.startIfFilesExist(this);
56521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    }
56621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
5679037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private void doRestoreFile(String fileName, FileDescriptor fd) throws IOException {
5688b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (DEBUG) {
56921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            Log.i(TAG, "Restoring file " + fileName);
57021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
57121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
57221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        try (JsonReader jsonReader = getJsonReader(fd)) {
57321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (fileName.endsWith(SMS_BACKUP_FILE_SUFFIX)) {
57421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                if (DEBUG) {
57521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    Log.i(TAG, "Restoring SMS");
57621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                }
5779037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                putSmsMessagesToProvider(jsonReader);
57821736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            } else if (fileName.endsWith(MMS_BACKUP_FILE_SUFFIX)) {
57921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                if (DEBUG) {
58021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    Log.i(TAG, "Restoring text MMS");
58121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                }
5829037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                putMmsMessagesToProvider(jsonReader);
58321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            } else {
58421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                if (DEBUG) {
58521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    Log.e(TAG, "Unknown file to restore:" + fileName);
58621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                }
58721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
5888b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
5898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
5908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
5918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
5929037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    void putSmsMessagesToProvider(JsonReader jsonReader) throws IOException {
5938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.beginArray();
59421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        int msgCount = 0;
5959037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        final int bulkInsertSize = mMaxMsgPerFile;
59621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        ContentValues[] values = new ContentValues[bulkInsertSize];
5978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        while (jsonReader.hasNext()) {
5989037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            ContentValues cv = readSmsValuesFromReader(jsonReader);
5999037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            if (doesSmsExist(cv)) {
6008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                continue;
6018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
60221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            values[(msgCount++) % bulkInsertSize] = cv;
60321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (msgCount % bulkInsertSize == 0) {
6049037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
60521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
60621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
60721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        if (msgCount % bulkInsertSize > 0) {
6089037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI,
60921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    Arrays.copyOf(values, msgCount % bulkInsertSize));
6108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.endArray();
6128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
6138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
6159037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    void putMmsMessagesToProvider(JsonReader jsonReader) throws IOException {
6168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.beginArray();
6178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        while (jsonReader.hasNext()) {
6189037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            final Mms mms = readMmsFromReader(jsonReader);
6199037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            if (doesMmsExist(mms)) {
6208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                if (DEBUG) {
6218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
6228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
6238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                continue;
6248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
6259037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            addMmsMessage(mms);
6268b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6278b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
6288b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6298b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @VisibleForTesting
6308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    static final String[] PROJECTION_ID = {BaseColumns._ID};
63121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static final int ID_IDX = 0;
6328b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6339037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private boolean doesSmsExist(ContentValues smsValues) {
63456117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        final String where = String.format(Locale.US, "%s = %d and %s = %s",
6358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
6368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                Telephony.Sms.BODY,
6378b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                DatabaseUtils.sqlEscapeString(smsValues.getAsString(Telephony.Sms.BODY)));
6389037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID, where,
6398b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                null, null)) {
6408b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return cursor != null && cursor.getCount() > 0;
6418b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6428b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
6438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6449037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private boolean doesMmsExist(Mms mms) {
64556117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        final String where = String.format(Locale.US, "%s = %d",
6468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                Telephony.Sms.DATE, mms.values.getAsLong(Telephony.Mms.DATE));
6479037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        try (Cursor cursor = mContentResolver.query(Telephony.Mms.CONTENT_URI, PROJECTION_ID, where,
6488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                null, null)) {
6498b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (cursor != null && cursor.moveToFirst()) {
6508b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                do {
65121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    final int mmsId = cursor.getInt(ID_IDX);
6529037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    final MmsBody body = getMmsBody(mmsId);
6538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (body != null && body.equals(mms.body)) {
6548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        return true;
6558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
6568b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                } while (cursor.moveToNext());
6578b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
6588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return false;
6608b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
6618b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6628b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static String getNormalizedNumber(SubscriptionInfo subscriptionInfo) {
6638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (subscriptionInfo == null) {
6648b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return null;
6658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6668b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return PhoneNumberUtils.formatNumberToE164(subscriptionInfo.getNumber(),
6678b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                subscriptionInfo.getCountryIso().toUpperCase(Locale.US));
6688b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
6698b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6709037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private void writeSmsToWriter(JsonWriter jsonWriter, Cursor cursor) throws IOException {
6718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.beginObject();
6728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
6738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        for (int i=0; i<cursor.getColumnCount(); ++i) {
6748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String name = cursor.getColumnName(i);
6758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String value = cursor.getString(i);
6768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (value == null) {
6778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                continue;
6788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
6798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            switch (name) {
6808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.SUBSCRIPTION_ID:
6818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    final int subId = cursor.getInt(i);
6829037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    final String selfNumber = mSubId2phone.get(subId);
6838b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (selfNumber != null) {
6848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        jsonWriter.name(SELF_PHONE_KEY).value(selfNumber);
6858b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
6868b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
687a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                case Telephony.Sms.THREAD_ID:
688a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    final long threadId = cursor.getLong(i);
6893aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    handleThreadId(jsonWriter, threadId);
690a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    break;
6918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms._ID:
6928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
6938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                default:
6948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    jsonWriter.name(name).value(value);
6958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
6968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
6978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
6988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.endObject();
699a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
700a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    }
701a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
7023aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private void handleThreadId(JsonWriter jsonWriter, long threadId) throws IOException {
70356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        final List<String> recipients = getRecipientsByThread(threadId);
70456117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        if (recipients == null || recipients.isEmpty()) {
70556117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            return;
70656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        }
70756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
70856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        writeRecipientsToWriter(jsonWriter.name(RECIPIENTS), recipients);
7093aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        if (!mThreadArchived.containsKey(threadId)) {
7103aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            boolean isArchived = isThreadArchived(threadId);
7113aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            if (isArchived) {
7123aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                jsonWriter.name(Telephony.Threads.ARCHIVED).value(true);
7133aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            }
7143aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            mThreadArchived.put(threadId, isArchived);
7153aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        }
7163aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    }
7173aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
7183aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private static String[] THREAD_ARCHIVED_PROJECTION =
7193aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            new String[] { Telephony.Threads.ARCHIVED };
7203aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private static int THREAD_ARCHIVED_IDX = 0;
7213aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
7223aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private boolean isThreadArchived(long threadId) {
7233aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        Uri.Builder builder = Telephony.Threads.CONTENT_URI.buildUpon();
7243aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        builder.appendPath(String.valueOf(threadId)).appendPath("recipients");
7253aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        Uri uri = builder.build();
7263aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
7273aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        try (Cursor cursor = getContentResolver().query(uri, THREAD_ARCHIVED_PROJECTION, null, null,
7283aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                null)) {
72956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            if (cursor != null && cursor.moveToFirst()) {
7303aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                return cursor.getInt(THREAD_ARCHIVED_IDX) == 1;
7313aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            }
7323aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        }
7333aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        return false;
7343aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    }
7353aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
73676422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin    private static void writeRecipientsToWriter(JsonWriter jsonWriter, List<String> recipients)
737a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            throws IOException {
738a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        jsonWriter.beginArray();
739a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        if (recipients != null) {
740a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            for (String s : recipients) {
741a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                jsonWriter.value(s);
742a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
743a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
744a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        jsonWriter.endArray();
7458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
7468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
7479037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private ContentValues readSmsValuesFromReader(JsonReader jsonReader)
7488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            throws IOException {
74956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        ContentValues values = new ContentValues(6+sDefaultValuesSms.size());
75021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        values.putAll(sDefaultValuesSms);
7513aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        long threadId = -1;
7523aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        boolean isArchived = false;
7538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.beginObject();
7548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        while (jsonReader.hasNext()) {
7558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            String name = jsonReader.nextName();
7568b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            switch (name) {
7578b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.BODY:
7588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.DATE:
7598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.DATE_SENT:
7608b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.STATUS:
7618b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.TYPE:
7628b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Sms.SUBJECT:
763a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                case Telephony.Sms.ADDRESS:
7648b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    values.put(name, jsonReader.nextString());
7658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
76676422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                case RECIPIENTS:
7673aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    threadId = getOrCreateThreadId(getRecipients(jsonReader));
7683aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    values.put(Telephony.Sms.THREAD_ID, threadId);
7693aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    break;
7703aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                case Telephony.Threads.ARCHIVED:
7713aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    isArchived = jsonReader.nextBoolean();
7728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
7738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case SELF_PHONE_KEY:
7748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    final String selfPhone = jsonReader.nextString();
7759037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    if (mPhone2subId.containsKey(selfPhone)) {
7769037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                        values.put(Telephony.Sms.SUBSCRIPTION_ID, mPhone2subId.get(selfPhone));
7778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
7788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
7798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                default:
7808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (DEBUG) {
7818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        Log.w(TAG, "Unknown name:" + name);
7828b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
7838b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    jsonReader.skipValue();
7848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
7858b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
7868b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
7878b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.endObject();
7883aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        archiveThread(threadId, isArchived);
7898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return values;
7908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
7918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
79276422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin    private static Set<String> getRecipients(JsonReader jsonReader) throws IOException {
7938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        Set<String> recipients = new ArraySet<String>();
794a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        jsonReader.beginArray();
795a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        while (jsonReader.hasNext()) {
796a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            recipients.add(jsonReader.nextString());
797a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
798a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        jsonReader.endArray();
7998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return recipients;
8008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
8018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
8029037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private int writeMmsToWriter(JsonWriter jsonWriter, Cursor cursor) throws IOException {
80321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        final int mmsId = cursor.getInt(ID_IDX);
8049037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        final MmsBody body = getMmsBody(mmsId);
8058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (body == null || body.text == null) {
8068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return 0;
8078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
8088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
8098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        boolean subjectNull = true;
8108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.beginObject();
8118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        for (int i=0; i<cursor.getColumnCount(); ++i) {
8128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String name = cursor.getColumnName(i);
8138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String value = cursor.getString(i);
8148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (value == null) {
8158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                continue;
8168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
8178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            switch (name) {
81876422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                case Telephony.Mms.SUBSCRIPTION_ID:
8198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    final int subId = cursor.getInt(i);
8209037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    final String selfNumber = mSubId2phone.get(subId);
8218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (selfNumber != null) {
8228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        jsonWriter.name(SELF_PHONE_KEY).value(selfNumber);
8238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
8248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
82576422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                case Telephony.Mms.THREAD_ID:
82676422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                    final long threadId = cursor.getLong(i);
8273aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    handleThreadId(jsonWriter, threadId);
82876422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                    break;
8298b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms._ID:
8308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.SUBJECT_CHARSET:
8318b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8328b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.SUBJECT:
8338b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    subjectNull = false;
8348b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                default:
8358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    jsonWriter.name(name).value(value);
8368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8378b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
8388b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
8398b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Addresses.
8409037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        writeMmsAddresses(jsonWriter.name(MMS_ADDRESSES_KEY), mmsId);
8418b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Body (text of the message).
8428b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.name(MMS_BODY_KEY).value(body.text);
8438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Charset of the body text.
8448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.name(MMS_BODY_CHARSET_KEY).value(body.charSet);
8458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
8468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (!subjectNull) {
8478b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            // Subject charset.
8488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            writeStringToWriter(jsonWriter, cursor, Telephony.Mms.SUBJECT_CHARSET);
8498b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
8508b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.endObject();
8518b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return 1;
8528b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
8538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
8549037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private Mms readMmsFromReader(JsonReader jsonReader) throws IOException {
8558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        Mms mms = new Mms();
85656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        mms.values = new ContentValues(5+sDefaultValuesMms.size());
85721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        mms.values.putAll(sDefaultValuesMms);
8588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.beginObject();
8598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        String bodyText = null;
8603aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        long threadId = -1;
8613aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        boolean isArchived = false;
8628b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        int bodyCharset = CharacterSets.DEFAULT_CHARSET;
8638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        while (jsonReader.hasNext()) {
8648b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            String name = jsonReader.nextName();
8658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            switch (name) {
8668b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case SELF_PHONE_KEY:
86776422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                    final String selfPhone = jsonReader.nextString();
8689037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                    if (mPhone2subId.containsKey(selfPhone)) {
8699037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                        mms.values.put(Telephony.Mms.SUBSCRIPTION_ID, mPhone2subId.get(selfPhone));
8708b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
8718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case MMS_ADDRESSES_KEY:
8738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    getMmsAddressesFromReader(jsonReader, mms);
8748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case MMS_BODY_KEY:
8768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    bodyText = jsonReader.nextString();
8778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case MMS_BODY_CHARSET_KEY:
8798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    bodyCharset = jsonReader.nextInt();
8808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
88176422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                case RECIPIENTS:
8823aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    threadId = getOrCreateThreadId(getRecipients(jsonReader));
8833aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    mms.values.put(Telephony.Sms.THREAD_ID, threadId);
8843aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    break;
8853aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                case Telephony.Threads.ARCHIVED:
8863aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                    isArchived = jsonReader.nextBoolean();
88776422e0ed1bc5b68f857094a05b5fec8b507f36eRoman Sorokin                    break;
8888b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.SUBJECT:
8898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.SUBJECT_CHARSET:
8908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.DATE:
8918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.DATE_SENT:
8928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.MESSAGE_TYPE:
8938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.MMS_VERSION:
8948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.MESSAGE_BOX:
8958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                case Telephony.Mms.CONTENT_LOCATION:
8963aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                case Telephony.Mms.TRANSACTION_ID:
8978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    mms.values.put(name, jsonReader.nextString());
8988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
8998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                default:
9008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (DEBUG) {
9018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        Log.w(TAG, "Unknown name:" + name);
9028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
9038b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    jsonReader.skipValue();
9048b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    break;
9058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
9068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
9078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.endObject();
9088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (bodyText != null) {
9108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            mms.body = new MmsBody(bodyText, bodyCharset);
9118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
9128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Set default charset for subject.
9148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (mms.values.get(Telephony.Mms.SUBJECT) != null &&
9158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                mms.values.get(Telephony.Mms.SUBJECT_CHARSET) == null) {
9168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            mms.values.put(Telephony.Mms.SUBJECT_CHARSET, CharacterSets.DEFAULT_CHARSET);
9178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
9188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9193aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        archiveThread(threadId, isArchived);
9203aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
9218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return mms;
9228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
9238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9243aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private static final String ARCHIVE_THREAD_SELECTION = Telephony.Threads._ID + "=?";
9253aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
9263aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    private void archiveThread(long threadId, boolean isArchived) {
9273aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        if (threadId < 0 || !isArchived) {
9283aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            return;
9293aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        }
9303aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        final ContentValues values = new ContentValues(1);
9313aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        values.put(Telephony.Threads.ARCHIVED, 1);
9323aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        if (mContentResolver.update(
9333aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                Telephony.Threads.CONTENT_URI,
9343aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                values,
9353aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                ARCHIVE_THREAD_SELECTION,
9363aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                new String[] { Long.toString(threadId)}) != 1) {
9373aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            if (DEBUG) {
9383aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin                Log.e(TAG, "archiveThread: failed to update database");
9393aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin            }
9403aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin        }
9413aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin    }
9423aa281731a6f25cd36b88bca809b13d3f581d05fRoman Sorokin
9439037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private MmsBody getMmsBody(int mmsId) {
9448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        Uri MMS_PART_CONTENT_URI = Telephony.Mms.CONTENT_URI.buildUpon()
9458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                .appendPath(String.valueOf(mmsId)).appendPath("part").build();
9468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9478b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        String body = null;
9488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        int charSet = 0;
9498b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9509037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        try (Cursor cursor = mContentResolver.query(MMS_PART_CONTENT_URI, MMS_TEXT_PROJECTION,
95121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                Telephony.Mms.Part.CONTENT_TYPE + "=?", new String[]{ContentType.TEXT_PLAIN},
95221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                ORDER_BY_ID)) {
9538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (cursor != null && cursor.moveToFirst()) {
9548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                do {
95521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    body = (body == null ? cursor.getString(MMS_TEXT_IDX)
95621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                            : body.concat(cursor.getString(MMS_TEXT_IDX)));
95721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                    charSet = cursor.getInt(MMS_TEXT_CHARSET_IDX);
9588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                } while (cursor.moveToNext());
9598b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
9608b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
9618b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return (body == null ? null : new MmsBody(body, charSet));
9628b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
9638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9649037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private void writeMmsAddresses(JsonWriter jsonWriter, int mmsId) throws IOException {
9658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        Uri.Builder builder = Telephony.Mms.CONTENT_URI.buildUpon();
9668b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        builder.appendPath(String.valueOf(mmsId)).appendPath("addr");
9678b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        Uri uriAddrPart = builder.build();
9688b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9698b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.beginArray();
9709037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        try (Cursor cursor = mContentResolver.query(uriAddrPart, MMS_ADDR_PROJECTION,
9718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                null/*selection*/, null/*selectionArgs*/, ORDER_BY_ID)) {
9728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (cursor != null && cursor.moveToFirst()) {
9738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                do {
9748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    if (cursor.getString(cursor.getColumnIndex(Telephony.Mms.Addr.ADDRESS))
9758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                            != null) {
9768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        jsonWriter.beginObject();
9778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        writeIntToWriter(jsonWriter, cursor, Telephony.Mms.Addr.TYPE);
9788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        writeStringToWriter(jsonWriter, cursor, Telephony.Mms.Addr.ADDRESS);
9798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        writeIntToWriter(jsonWriter, cursor, Telephony.Mms.Addr.CHARSET);
9808b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        jsonWriter.endObject();
9818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    }
9828b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                } while (cursor.moveToNext());
9838b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
9848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
9858b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonWriter.endArray();
9868b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
9878b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
9888b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static void getMmsAddressesFromReader(JsonReader jsonReader, Mms mms)
9898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            throws IOException {
9908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        mms.addresses = new ArrayList<ContentValues>();
9918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.beginArray();
9928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        while (jsonReader.hasNext()) {
9938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            jsonReader.beginObject();
99421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            ContentValues addrValues = new ContentValues(sDefaultValuesAddr);
9958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            while (jsonReader.hasNext()) {
9968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                final String name = jsonReader.nextName();
9978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                switch (name) {
9988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    case Telephony.Mms.Addr.TYPE:
9998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    case Telephony.Mms.Addr.CHARSET:
10008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        addrValues.put(name, jsonReader.nextInt());
10018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        break;
10028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    case Telephony.Mms.Addr.ADDRESS:
10038b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        addrValues.put(name, jsonReader.nextString());
10048b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        break;
10058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    default:
10068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        if (DEBUG) {
10078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                            Log.w(TAG, "Unknown name:" + name);
10088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        }
10098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        jsonReader.skipValue();
10108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                        break;
10118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
10128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            jsonReader.endObject();
10148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (addrValues.containsKey(Telephony.Mms.Addr.ADDRESS)) {
10158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                mms.addresses.add(addrValues);
10168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        jsonReader.endArray();
10198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
10208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10219037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private void addMmsMessage(Mms mms) {
10228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (DEBUG) {
10238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            Log.e(TAG, "Add mms:\n" + mms.toString());
10248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10258b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
10268b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final Uri partUri = Telephony.Mms.CONTENT_URI.buildUpon()
10278b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                .appendPath(String.valueOf(dummyId)).appendPath("part").build();
10288b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
102956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        final String srcName = String.format(Locale.US, "text.%06d.txt", 0);
10308b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        { // Insert SMIL part.
10318b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String smilBody = String.format(sSmilTextPart, srcName);
10328b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final String smil = String.format(sSmilTextOnly, smilBody);
10338b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final ContentValues values = new ContentValues(7);
10348b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.MSG_ID, dummyId);
10358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.SEQ, -1);
10368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.APP_SMIL);
10378b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.NAME, "smil.xml");
10388b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_ID, "<smil>");
10398b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_LOCATION, "smil.xml");
10408b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.TEXT, smil);
10419037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            if (mContentResolver.insert(partUri, values) == null) {
10428b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                if (DEBUG) {
10438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    Log.e(TAG, "Could not insert SMIL part");
10448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
10458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                return;
10468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10478b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10498b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        { // Insert body part.
10508b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final ContentValues values = new ContentValues(8);
10518b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.MSG_ID, dummyId);
10528b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.SEQ, 0);
10538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.TEXT_PLAIN);
10548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.NAME, srcName);
10558b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_ID, "<"+srcName+">");
10568b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CONTENT_LOCATION, srcName);
10578b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.CHARSET, mms.body.charSet);
10588b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.TEXT, mms.body.text);
10599037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            if (mContentResolver.insert(partUri, values) == null) {
10608b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                if (DEBUG) {
10618b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    Log.e(TAG, "Could not insert body part");
10628b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                }
10638b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                return;
10648b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10658b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10668b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10678b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Insert mms.
10689037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        final Uri mmsUri = mContentResolver.insert(Telephony.Mms.CONTENT_URI, mms.values);
10698b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (mmsUri == null) {
10708b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (DEBUG) {
10718b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                Log.e(TAG, "Could not insert mms");
10728b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10738b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return;
10748b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10758b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10768b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final long mmsId = ContentUris.parseId(mmsUri);
10778b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        { // Update parts with the right mms id.
10788b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            ContentValues values = new ContentValues(1);
10798b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            values.put(Telephony.Mms.Part.MSG_ID, mmsId);
10809037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mContentResolver.update(partUri, values, null, null);
10818b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10828b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10838b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        { // Insert adderesses into "addr".
10848b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            final Uri addrUri = Uri.withAppendedPath(mmsUri, "addr");
10858b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            for (ContentValues mmsAddress : mms.addresses) {
10868b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                ContentValues values = new ContentValues(mmsAddress);
10878b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                values.put(Telephony.Mms.Addr.MSG_ID, mmsId);
10889037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mContentResolver.insert(addrUri, values);
10898b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
10908b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
10918b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
10928b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10938b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final class MmsBody {
10948b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public String text;
10958b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public int charSet;
10968b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
10978b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public MmsBody(String text, int charSet) {
10988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            this.text = text;
10998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            this.charSet = charSet;
11008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        @Override
11038b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public boolean equals(Object obj) {
11048b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            if (obj == null || !(obj instanceof MmsBody)) {
11058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                return false;
11068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            }
11078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            MmsBody typedObj = (MmsBody) obj;
11088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return this.text.equals(typedObj.text) && this.charSet == typedObj.charSet;
11098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11108b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11118b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        @Override
11128b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public String toString() {
11138b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return "Text:" + text + " charSet:" + charSet;
11148b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11158b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11168b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11178b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static final class Mms {
11188b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public ContentValues values;
11198b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public List<ContentValues> addresses;
11208b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public MmsBody body;
11218b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        @Override
11228b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        public String toString() {
11238b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            return "Values:" + values.toString() + "\nRecipients:"+addresses.toString()
11248b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                    + "\nBody:" + body;
11258b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11268b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11278b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11288b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private JsonWriter getJsonWriter(final String fileName) throws IOException {
112921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        return new JsonWriter(new BufferedWriter(new OutputStreamWriter(new DeflaterOutputStream(
113021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin                openFileOutput(fileName, MODE_PRIVATE)), CHARSET_UTF8), WRITER_BUFFER_SIZE));
11318b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11328b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
113321736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    private static JsonReader getJsonReader(final FileDescriptor fileDescriptor)
113421736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            throws IOException {
11358b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        return new JsonReader(new InputStreamReader(new InflaterInputStream(
11368b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                new FileInputStream(fileDescriptor)), CHARSET_UTF8));
11378b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11388b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11398b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static void writeStringToWriter(JsonWriter jsonWriter, Cursor cursor, String name)
11408b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            throws IOException {
11418b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final String value = cursor.getString(cursor.getColumnIndex(name));
11428b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (value != null) {
11438b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            jsonWriter.name(name).value(value);
11448b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11458b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11468b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11478b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    private static void writeIntToWriter(JsonWriter jsonWriter, Cursor cursor, String name)
11488b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            throws IOException {
11498b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        final int value = cursor.getInt(cursor.getColumnIndex(name));
11508b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        if (value != 0) {
11518b39acf181c547e87161873112d6c52a581fc778Roman Sorokin            jsonWriter.name(name).value(value);
11528b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        }
11538b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
11548b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
11559037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private long getOrCreateThreadId(Set<String> recipients) {
115656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        if (recipients == null) {
115756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            recipients = new ArraySet<String>();
115856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        }
115956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
116056117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        if (recipients.isEmpty()) {
116156117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            recipients.add(UNKNOWN_SENDER);
116256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin        }
116356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin
11649037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        if (mCacheGetOrCreateThreadId == null) {
11659037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mCacheGetOrCreateThreadId = new HashMap<>();
116621736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
116721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
11689037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        if (!mCacheGetOrCreateThreadId.containsKey(recipients)) {
116956117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            long threadId = mUnknownSenderThreadId;
117056117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            try {
117156117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                threadId = Telephony.Threads.getOrCreateThreadId(this, recipients);
117256117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            } catch (RuntimeException e) {
117356117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                if (DEBUG) {
117456117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                    Log.e(TAG, e.toString());
117556117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin                }
117656117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            }
117756117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            mCacheGetOrCreateThreadId.put(recipients, threadId);
117856117b955dda1c134473a4fed4b0a73fb1c6cea2Roman Sorokin            return threadId;
117921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
118021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
11819037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        return mCacheGetOrCreateThreadId.get(recipients);
11828b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
1183a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
11849037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    @VisibleForTesting
11859037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    static final Uri THREAD_ID_CONTENT_URI = Uri.parse("content://mms-sms/threadID");
11869037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin
118721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin    // Mostly copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
11889037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private List<String> getRecipientsByThread(final long threadId) {
11899037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        if (mCacheRecipientsByThread == null) {
11909037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            mCacheRecipientsByThread = new HashMap<>();
1191a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
119221736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
11939037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        if (!mCacheRecipientsByThread.containsKey(threadId)) {
11949037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin            final String spaceSepIds = getRawRecipientIdsForThread(threadId);
119521736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            if (!TextUtils.isEmpty(spaceSepIds)) {
11969037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mCacheRecipientsByThread.put(threadId, getAddresses(spaceSepIds));
119721736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            } else {
11989037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                mCacheRecipientsByThread.put(threadId, new ArrayList<String>());
119921736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin            }
120021736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin        }
120121736c3871d5c4cd3a6322d65d635057d0a61937Roman Sorokin
12029037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        return mCacheRecipientsByThread.get(threadId);
1203a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    }
1204a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
12051d42223c7e497b5513fa0cc5e65ffc405b432989Roman Sorokin    @VisibleForTesting
12061d42223c7e497b5513fa0cc5e65ffc405b432989Roman Sorokin    static final Uri ALL_THREADS_URI =
1207a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Telephony.Threads.CONTENT_URI.buildUpon().
1208a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    appendQueryParameter("simple", "true").build();
1209a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    private static final int RECIPIENT_IDS  = 1;
1210a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
1211a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
1212a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    // NOTE: There are phones on which you can't get the recipients from the thread id for SMS
1213a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    // until you have a message in the conversation!
12149037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private String getRawRecipientIdsForThread(final long threadId) {
1215a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        if (threadId <= 0) {
1216a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            return null;
1217a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
12189037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin        final Cursor thread = mContentResolver.query(
1219a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                ALL_THREADS_URI,
1220a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                SMS_RECIPIENTS_PROJECTION, "_id=?", new String[]{String.valueOf(threadId)}, null);
1221a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        if (thread != null) {
1222a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            try {
1223a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                if (thread.moveToFirst()) {
1224a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    // recipientIds will be a space-separated list of ids into the
1225a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    // canonical addresses table.
1226a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    return thread.getString(RECIPIENT_IDS);
1227a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                }
1228a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            } finally {
1229a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                thread.close();
1230a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
1231a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
1232a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        return null;
1233a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    }
1234a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
12351d42223c7e497b5513fa0cc5e65ffc405b432989Roman Sorokin    @VisibleForTesting
12361d42223c7e497b5513fa0cc5e65ffc405b432989Roman Sorokin    static final Uri SINGLE_CANONICAL_ADDRESS_URI =
1237a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Uri.parse("content://mms-sms/canonical-address");
1238a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
1239a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
12409037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin    private List<String> getAddresses(final String spaceSepIds) {
1241a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        final List<String> numbers = new ArrayList<String>();
1242a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        final String[] ids = spaceSepIds.split(" ");
1243a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        for (final String id : ids) {
1244a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            long longId;
1245a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
1246a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            try {
1247a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                longId = Long.parseLong(id);
1248a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                if (longId < 0) {
1249a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    if (DEBUG) {
1250a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        Log.e(TAG, "getAddresses: invalid id " + longId);
1251a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    }
1252a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    continue;
1253a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                }
1254a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            } catch (final NumberFormatException ex) {
1255a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                if (DEBUG) {
1256a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    Log.e(TAG, "getAddresses: invalid id. " + ex, ex);
1257a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                }
1258a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                // skip this id
1259a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                continue;
1260a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
1261a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
1262a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            // TODO: build a single query where we get all the addresses at once.
1263a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            Cursor c = null;
1264a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            try {
12659037d648d61f3d90726e43296f0b12c532e1f934Roman Sorokin                c = mContentResolver.query(
1266a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        ContentUris.withAppendedId(SINGLE_CANONICAL_ADDRESS_URI, longId),
1267a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        null, null, null, null);
1268a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            } catch (final Exception e) {
1269a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                if (DEBUG) {
1270a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    Log.e(TAG, "getAddresses: query failed for id " + longId, e);
1271a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                }
1272a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
1273a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            if (c != null) {
1274a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                try {
1275a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    if (c.moveToFirst()) {
1276a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        final String number = c.getString(0);
1277a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        if (!TextUtils.isEmpty(number)) {
1278a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                            numbers.add(number);
1279a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        } else {
1280a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                            if (DEBUG) {
1281a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                                Log.w(TAG, "Canonical MMS/SMS address is empty for id: " + longId);
1282a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                            }
1283a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                        }
1284a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    }
1285a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                } finally {
1286a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                    c.close();
1287a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                }
1288a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
1289a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
1290a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        if (numbers.isEmpty()) {
1291a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            if (DEBUG) {
1292a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin                Log.w(TAG, "No MMS addresses found from ids string [" + spaceSepIds + "]");
1293a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin            }
1294a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        }
1295a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin        return numbers;
1296a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin    }
1297a632476534b877cdad602a27fe8ba01892d0bd83Roman Sorokin
12988b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @Override
12998b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
13008b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                         ParcelFileDescriptor newState) throws IOException {
13018b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Empty because is not used during full backup.
13028b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
13038b39acf181c547e87161873112d6c52a581fc778Roman Sorokin
13048b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    @Override
13058b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    public void onRestore(BackupDataInput data, int appVersionCode,
13068b39acf181c547e87161873112d6c52a581fc778Roman Sorokin                          ParcelFileDescriptor newState) throws IOException {
13078b39acf181c547e87161873112d6c52a581fc778Roman Sorokin        // Empty because is not used during full restore.
13088b39acf181c547e87161873112d6c52a581fc778Roman Sorokin    }
13098b39acf181c547e87161873112d6c52a581fc778Roman Sorokin}
1310