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