1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel.action; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteException; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Bundle; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Parcel; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Parcelable; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.SystemClock; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony.Mms; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.util.LongSparseArray; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DataModel; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DatabaseWrapper; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.MessagingContentProvider; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.SyncManager; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.SyncManager.ThreadInfoCache; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ParticipantData; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.SqliteWrapper; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.DatabaseMessages; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.DatabaseMessages.LocalDatabaseMessage; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.DatabaseMessages.MmsMessage; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.DatabaseMessages.SmsMessage; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.sms.MmsUtils; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservices; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservicesKeys; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BuglePrefs; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BuglePrefsKeys; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ContentType; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil; 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Locale; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Action used to sync messages from smsmms db to local database 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class SyncMessagesAction extends Action implements Parcelable { 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static final long SYNC_FAILED = Long.MIN_VALUE; 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG; 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String KEY_START_TIMESTAMP = "start_timestamp"; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String KEY_MAX_UPDATE = "max_update"; 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String KEY_LOWER_BOUND = "lower_bound"; 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String KEY_UPPER_BOUND = "upper_bound"; 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String BUNDLE_KEY_LAST_TIMESTAMP = "last_timestamp"; 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String BUNDLE_KEY_SMS_MESSAGES = "sms_to_add"; 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String BUNDLE_KEY_MMS_MESSAGES = "mms_to_add"; 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String BUNDLE_KEY_MESSAGES_TO_DELETE = "messages_to_delete"; 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Start a full sync (backed off a few seconds to avoid pulling sending/receiving messages). 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void fullSync() { 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BugleGservices bugleGservices = BugleGservices.get(); 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long smsSyncBackoffTimeMillis = bugleGservices.getLong( 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS, 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT); 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long now = System.currentTimeMillis(); 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Could base this off most recent message in db but now should be okay... 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimestamp = now - smsSyncBackoffTimeMillis; 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction action = new SyncMessagesAction(-1L, startTimestamp, 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0, startTimestamp); 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd action.start(); 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Start an incremental sync to pull messages since last sync (backed off a few seconds).. 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void sync() { 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BugleGservices bugleGservices = BugleGservices.get(); 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long smsSyncBackoffTimeMillis = bugleGservices.getLong( 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS, 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT); 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long now = System.currentTimeMillis(); 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Could base this off most recent message in db but now should be okay... 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimestamp = now - smsSyncBackoffTimeMillis; 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sync(startTimestamp); 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Start an incremental sync when the application starts up (no back off as not yet 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * sending/receiving). 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void immediateSync() { 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long now = System.currentTimeMillis(); 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Could base this off most recent message in db but now should be okay... 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimestamp = now; 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sync(startTimestamp); 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static void sync(final long startTimestamp) { 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!OsUtil.hasSmsPermission()) { 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Sync requires READ_SMS permission 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getApplicationPrefs(); 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Lower bound is end of previous sync 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long syncLowerBoundTimeMillis = prefs.getLong(BuglePrefsKeys.LAST_SYNC_TIME, 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BuglePrefsKeys.LAST_SYNC_TIME_DEFAULT); 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction action = new SyncMessagesAction(syncLowerBoundTimeMillis, 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startTimestamp, 0, startTimestamp); 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd action.start(); 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SyncMessagesAction(final long lowerBound, final long upperBound, 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxMessagesToUpdate, final long startTimestamp) { 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionParameters.putLong(KEY_LOWER_BOUND, lowerBound); 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionParameters.putLong(KEY_UPPER_BOUND, upperBound); 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionParameters.putInt(KEY_MAX_UPDATE, maxMessagesToUpdate); 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionParameters.putLong(KEY_START_TIMESTAMP, startTimestamp); 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected Object executeAction() { 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db = DataModel.get().getDatabase(); 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long lowerBoundTimeMillis = actionParameters.getLong(KEY_LOWER_BOUND); 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long upperBoundTimeMillis = actionParameters.getLong(KEY_UPPER_BOUND); 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int initialMaxMessagesToUpdate = actionParameters.getInt(KEY_MAX_UPDATE); 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimestamp = actionParameters.getLong(KEY_START_TIMESTAMP); 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Request to sync messages from " 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " to " + upperBoundTimeMillis + " (start timestamp = " 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + startTimestamp + ", message update limit = " + initialMaxMessagesToUpdate 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ")"); 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncManager syncManager = DataModel.get().getSyncManager(); 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lowerBoundTimeMillis >= 0) { 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Cursors 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncCursorPair cursors = new SyncCursorPair(-1L, lowerBoundTimeMillis); 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean inSync = cursors.isSynchronized(db); 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!inSync) { 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (syncManager.delayUntilFullSync(startTimestamp) == 0) { 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lowerBoundTimeMillis = -1; 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionParameters.putLong(KEY_LOWER_BOUND, lowerBoundTimeMillis); 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Messages before " 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " not in sync; promoting to full sync"); 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Messages before " 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " not in sync; will do incremental sync"); 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Messages before " + lowerBoundTimeMillis 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " are in sync"); 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check if sync allowed (can be too soon after last or one is already running) 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (syncManager.shouldSync(lowerBoundTimeMillis < 0, startTimestamp)) { 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.startSyncBatch(upperBoundTimeMillis); 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestBackgroundWork(); 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected Bundle doBackgroundWork() { 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BugleGservices bugleGservices = BugleGservices.get(); 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db = DataModel.get().getDatabase(); 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxMessagesToScan = bugleGservices.getInt( 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN, 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN_DEFAULT); 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int initialMaxMessagesToUpdate = actionParameters.getInt(KEY_MAX_UPDATE); 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int smsSyncSubsequentBatchSizeMin = bugleGservices.getInt( 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MIN, 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MIN_DEFAULT); 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int smsSyncSubsequentBatchSizeMax = bugleGservices.getInt( 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MAX, 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MAX_DEFAULT); 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Cap sync size to GServices limits 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxMessagesToUpdate = Math.max(smsSyncSubsequentBatchSizeMin, 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Math.min(initialMaxMessagesToUpdate, smsSyncSubsequentBatchSizeMax)); 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long lowerBoundTimeMillis = actionParameters.getLong(KEY_LOWER_BOUND); 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long upperBoundTimeMillis = actionParameters.getLong(KEY_UPPER_BOUND); 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(TAG, "SyncMessagesAction: Starting batch for messages from " 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " to " + upperBoundTimeMillis 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " (message update limit = " + maxMessagesToUpdate + ", message scan limit = " 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + maxMessagesToScan + ")"); 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Clear last change time so that we can work out if this batch is dirty when it completes 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncManager syncManager = DataModel.get().getSyncManager(); 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Clear the singleton cache that maps threads to recipients and to conversations. 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncManager.ThreadInfoCache cache = syncManager.getThreadInfoCache(); 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cache.clear(); 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Sms messages to store 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<SmsMessage> smsToAdd = new ArrayList<SmsMessage>(); 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Mms messages to store 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LongSparseArray<MmsMessage> mmsToAdd = new LongSparseArray<MmsMessage>(); 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // List of local SMS/MMS to remove 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<LocalDatabaseMessage> messagesToDelete = 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new ArrayList<LocalDatabaseMessage>(); 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long lastTimestampMillis = SYNC_FAILED; 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (syncManager.isSyncing(upperBoundTimeMillis)) { 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Cursors 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncCursorPair cursors = new SyncCursorPair(lowerBoundTimeMillis, 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd upperBoundTimeMillis); 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Actually compare the messages using cursor pair 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lastTimestampMillis = syncCursorPair(db, cursors, smsToAdd, mmsToAdd, 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagesToDelete, maxMessagesToScan, maxMessagesToUpdate, cache); 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Bundle response = new Bundle(); 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If comparison succeeds bundle up the changes for processing in ActionService 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lastTimestampMillis > SYNC_FAILED) { 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<MmsMessage> mmsToAddList = new ArrayList<MmsMessage>(); 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < mmsToAdd.size(); i++) { 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MmsMessage mms = mmsToAdd.valueAt(i); 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mmsToAddList.add(mms); 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.putParcelableArrayList(BUNDLE_KEY_SMS_MESSAGES, smsToAdd); 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.putParcelableArrayList(BUNDLE_KEY_MMS_MESSAGES, mmsToAddList); 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.putParcelableArrayList(BUNDLE_KEY_MESSAGES_TO_DELETE, messagesToDelete); 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.putLong(BUNDLE_KEY_LAST_TIMESTAMP, lastTimestampMillis); 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return response; 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Compare messages based on timestamp and uri 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param db local database wrapper 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param cursors cursor pair holding references to local and remote messages 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param smsToAdd newly found sms messages to add 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param mmsToAdd newly found mms messages to add 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param messagesToDelete messages not found needing deletion 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param maxMessagesToScan max messages to scan for changes 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param maxMessagesToUpdate max messages to return for updates 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param cache cache for conversation id / thread id / recipient set mapping 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return timestamp of the oldest message seen during the sync scan 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private long syncCursorPair(final DatabaseWrapper db, final SyncCursorPair cursors, 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<SmsMessage> smsToAdd, final LongSparseArray<MmsMessage> mmsToAdd, 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<LocalDatabaseMessage> messagesToDelete, final int maxMessagesToScan, 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxMessagesToUpdate, final ThreadInfoCache cache) { 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long lastTimestampMillis; 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimeMillis = SystemClock.elapsedRealtime(); 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Number of messages scanned local and remote 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int localPos = 0; 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int remotePos = 0; 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int localTotal = 0; 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int remoteTotal = 0; 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Scan through the messages on both sides and prepare messages for local message table 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // changes (including adding and deleting) 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursors.query(db); 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd localTotal = cursors.getLocalCount(); 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd remoteTotal = cursors.getRemoteCount(); 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Scanning cursors (local count = " + localTotal 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ", remote count = " + remoteTotal + ", message update limit = " 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + maxMessagesToUpdate + ", message scan limit = " + maxMessagesToScan 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + ")"); 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lastTimestampMillis = cursors.scan(maxMessagesToScan, maxMessagesToUpdate, 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd smsToAdd, mmsToAdd, messagesToDelete, cache); 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd localPos = cursors.getLocalPosition(); 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd remotePos = cursors.getRemotePosition(); 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Scanned cursors (local position = " + localPos 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " of " + localTotal + ", remote position = " + remotePos + " of " 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + remoteTotal + ")"); 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Batch loading the parts of the MMS messages in this batch 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd loadMmsParts(mmsToAdd); 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Lookup senders for incoming mms messages 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setMmsSenders(mmsToAdd, cache); 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final SQLiteException e) { 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SyncMessagesAction: Database exception", e); 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Let's abort 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lastTimestampMillis = SYNC_FAILED; 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (final Exception e) { 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We want to catch anything unexpected since this is running in a separate thread 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // and any unexpected exception will just fail this thread silently. 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Let's crash for dogfooders! 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.wtf(TAG, "SyncMessagesAction: unexpected failure in scan", e); 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lastTimestampMillis = SYNC_FAILED; 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursors != null) { 333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursors.close(); 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long endTimeMillis = SystemClock.elapsedRealtime(); 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: Scan complete (took " 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + (endTimeMillis - startTimeMillis) + " ms). " + smsToAdd.size() 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + " remote SMS to add, " + mmsToAdd.size() + " MMS to add, " 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + messagesToDelete.size() + " local messages to delete. " 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "Oldest timestamp seen = " + lastTimestampMillis); 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return lastTimestampMillis; 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Perform local database updates and schedule follow on sync actions 352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected Object processBackgroundResponse(final Bundle response) { 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long lastTimestampMillis = response.getLong(BUNDLE_KEY_LAST_TIMESTAMP); 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long lowerBoundTimeMillis = actionParameters.getLong(KEY_LOWER_BOUND); 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long upperBoundTimeMillis = actionParameters.getLong(KEY_UPPER_BOUND); 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxMessagesToUpdate = actionParameters.getInt(KEY_MAX_UPDATE); 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimestamp = actionParameters.getLong(KEY_START_TIMESTAMP); 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Check with the sync manager if any conflicting updates have been made to databases 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncManager syncManager = DataModel.get().getSyncManager(); 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean orphan = !syncManager.isSyncing(upperBoundTimeMillis); 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // lastTimestampMillis used to indicate failure 366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (orphan) { 367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This batch does not match current in progress timestamp. 368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "SyncMessagesAction: Ignoring orphan sync batch for messages from " 369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " to " + upperBoundTimeMillis); 370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean dirty = syncManager.isBatchDirty(lastTimestampMillis); 372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lastTimestampMillis == SYNC_FAILED) { 373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.e(TAG, "SyncMessagesAction: Sync failed - terminating"); 374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Failed - update last sync times to throttle our failure rate 376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getApplicationPrefs(); 377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Save sync completion time so next sync will start from here 378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd prefs.putLong(BuglePrefsKeys.LAST_SYNC_TIME, startTimestamp); 379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Remember last full sync so that don't start background full sync right away 380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd prefs.putLong(BuglePrefsKeys.LAST_FULL_SYNC_TIME, startTimestamp); 381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.complete(); 383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (dirty) { 384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "SyncMessagesAction: Redoing dirty sync batch of messages from " 385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + lowerBoundTimeMillis + " to " + upperBoundTimeMillis); 386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Redo this batch 388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction nextBatch = 389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new SyncMessagesAction(lowerBoundTimeMillis, upperBoundTimeMillis, 390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd maxMessagesToUpdate, startTimestamp); 391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.startSyncBatch(upperBoundTimeMillis); 393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestBackgroundWork(nextBatch); 394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Succeeded 396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<SmsMessage> smsToAdd = 397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.getParcelableArrayList(BUNDLE_KEY_SMS_MESSAGES); 398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<MmsMessage> mmsToAdd = 399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.getParcelableArrayList(BUNDLE_KEY_MMS_MESSAGES); 400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ArrayList<LocalDatabaseMessage> messagesToDelete = 401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd response.getParcelableArrayList(BUNDLE_KEY_MESSAGES_TO_DELETE); 402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int messagesUpdated = smsToAdd.size() + mmsToAdd.size() 404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + messagesToDelete.size(); 405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Perform local database changes in one transaction 407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd long txnTimeMillis = 0; 408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messagesUpdated > 0) { 409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long startTimeMillis = SystemClock.elapsedRealtime(); 410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessageBatch batch = new SyncMessageBatch(smsToAdd, mmsToAdd, 411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messagesToDelete, syncManager.getThreadInfoCache()); 412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batch.updateLocalDatabase(); 413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long endTimeMillis = SystemClock.elapsedRealtime(); 414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd txnTimeMillis = endTimeMillis - startTimeMillis; 415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(TAG, "SyncMessagesAction: Updated local database " 417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "(took " + txnTimeMillis + " ms). Added " 418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + smsToAdd.size() + " SMS, added " + mmsToAdd.size() + " MMS, deleted " 419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + messagesToDelete.size() + " messages."); 420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Investigate whether we can make this more fine-grained. 422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyEverythingChanged(); 423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: No local database updates to make"); 426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!syncManager.getHasFirstSyncCompleted()) { 429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If we have never completed a sync before (fresh install) and there are 430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // no messages, still inform the UI of a change so it can update syncing 431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // messages shown to the user 432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyConversationListChanged(); 433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagingContentProvider.notifyPartsChanged(); 434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Determine if there are more messages that need to be scanned 437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lastTimestampMillis >= 0 && lastTimestampMillis >= lowerBoundTimeMillis) { 438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(TAG, "SyncMessagesAction: More messages to sync; scheduling next " 440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "sync batch now."); 441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Include final millisecond of last sync in next sync 444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long newUpperBoundTimeMillis = lastTimestampMillis + 1; 445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int newMaxMessagesToUpdate = nextBatchSize(messagesUpdated, 446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd txnTimeMillis); 447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction nextBatch = 449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new SyncMessagesAction(lowerBoundTimeMillis, newUpperBoundTimeMillis, 450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd newMaxMessagesToUpdate, startTimestamp); 451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Proceed with next batch 453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.startSyncBatch(newUpperBoundTimeMillis); 454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestBackgroundWork(nextBatch); 455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BuglePrefs prefs = BuglePrefs.getApplicationPrefs(); 457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Save sync completion time so next sync will start from here 458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd prefs.putLong(BuglePrefsKeys.LAST_SYNC_TIME, startTimestamp); 459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lowerBoundTimeMillis < 0) { 460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Remember last full sync so that don't start another full sync right away 461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd prefs.putLong(BuglePrefsKeys.LAST_FULL_SYNC_TIME, startTimestamp); 462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long now = System.currentTimeMillis(); 465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // After any sync check if new messages have arrived 467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncCursorPair recents = new SyncCursorPair(startTimestamp, now); 468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncCursorPair olders = new SyncCursorPair(-1L, startTimestamp); 469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseWrapper db = DataModel.get().getDatabase(); 470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!recents.isSynchronized(db)) { 471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(TAG, "SyncMessagesAction: Changed messages after sync; " 472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "scheduling an incremental sync now."); 473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Just add a new batch for recent messages 475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction nextBatch = 476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new SyncMessagesAction(startTimestamp, now, 0, startTimestamp); 477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.startSyncBatch(now); 478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestBackgroundWork(nextBatch); 479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // After partial sync verify sync state 480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (lowerBoundTimeMillis >= 0 && !olders.isSynchronized(db)) { 481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Add a batch going back to start of time 482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "SyncMessagesAction: Changed messages before sync batch; " 483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "scheduling a full sync now."); 484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SyncMessagesAction nextBatch = 486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new SyncMessagesAction(-1L, startTimestamp, 0, startTimestamp); 487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.startSyncBatch(startTimestamp); 489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestBackgroundWork(nextBatch); 490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.i(TAG, "SyncMessagesAction: All messages now in sync"); 492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // All done, in sync 494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd syncManager.complete(); 495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Either sync should be complete or we should have a follow up request 498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(hasBackgroundActions() || !syncManager.isSyncing()); 499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Decide the next batch size based on the stats we collected with past batch 507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param messagesUpdated number of messages updated in this batch 508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param txnTimeMillis time the transaction took in ms 509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return Target number of messages to sync for next batch 510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static int nextBatchSize(final int messagesUpdated, final long txnTimeMillis) { 512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BugleGservices bugleGservices = BugleGservices.get(); 513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long smsSyncSubsequentBatchTimeLimitMillis = bugleGservices.getLong( 514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_TIME_LIMIT_MILLIS, 515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BugleGservicesKeys.SMS_SYNC_BATCH_TIME_LIMIT_MILLIS_DEFAULT); 516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (txnTimeMillis <= 0) { 518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Number of messages we can sync within the batch time limit using 521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the average sync time calculated based on the stats we collected 522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // in previous batch 523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (int) ((double) (messagesUpdated) / (double) txnTimeMillis 524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * smsSyncSubsequentBatchTimeLimitMillis); 525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Batch loading MMS parts for the messages in current batch 529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void loadMmsParts(final LongSparseArray<MmsMessage> mmses) { 531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = Factory.get().getApplicationContext(); 532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int totalIds = mmses.size(); 533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int start = 0; start < totalIds; start += MmsUtils.MAX_IDS_PER_QUERY) { 534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int end = Math.min(start + MmsUtils.MAX_IDS_PER_QUERY, totalIds); //excluding 535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = end - start; 536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String batchSelection = String.format( 537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Locale.US, 538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd "%s != '%s' AND %s IN %s", 539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Part.CONTENT_TYPE, 540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ContentType.APP_SMIL, 541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Mms.Part.MSG_ID, 542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsUtils.getSqlInOperand(count)); 543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String[] batchSelectionArgs = new String[count]; 544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < count; i++) { 545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batchSelectionArgs[i] = Long.toString(mmses.valueAt(start + i).getId()); 546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor = SqliteWrapper.query( 548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context, 549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd context.getContentResolver(), 550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MmsUtils.MMS_PART_CONTENT_URI, 551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsPart.PROJECTION, 552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batchSelection, 553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd batchSelectionArgs, 554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null/*sortOrder*/); 555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd while (cursor.moveToNext()) { 558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delay loading the media content for parsing for efficiency 559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: load the media and fill in the dimensions when 560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // we actually display it 561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseMessages.MmsPart part = 562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DatabaseMessages.MmsPart.get(cursor, false/*loadMedia*/); 563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DatabaseMessages.MmsMessage mms = mmses.get(part.mMessageId); 564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mms != null) { 565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.addPart(part); 566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } finally { 569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cursor.close(); 570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Batch loading MMS sender for the messages in current batch 577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void setMmsSenders(final LongSparseArray<MmsMessage> mmses, 579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ThreadInfoCache cache) { 580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Store all the MMS messages 581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (int i = 0; i < mmses.size(); i++) { 582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MmsMessage mms = mmses.valueAt(i); 583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isOutgoing = mms.mType != Mms.MESSAGE_BOX_INBOX; 585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String senderId = null; 586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isOutgoing) { 587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We only need to find out sender phone number for received message 588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd senderId = getMmsSender(mms, cache); 589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (senderId == null) { 590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "SyncMessagesAction: Could not find sender of incoming MMS " 591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "message " + mms.getUri() + "; using 'unknown sender' instead"); 592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd senderId = ParticipantData.getUnknownSenderDestination(); 593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mms.setSender(senderId); 596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Find out the sender of an MMS message 601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private String getMmsSender(final MmsMessage mms, final ThreadInfoCache cache) { 603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<String> recipients = cache.getThreadRecipients(mms.mThreadId); 604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(recipients); 605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(recipients.size() > 0); 606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (recipients.size() == 1 608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd && recipients.get(0).equals(ParticipantData.getUnknownSenderDestination())) { 609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(TAG, "SyncMessagesAction: MMS message " + mms.mUri + " has unknown sender " 610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd + "(thread id = " + mms.mThreadId + ")"); 611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return MmsUtils.getMmsSender(recipients, mms.mUri); 614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SyncMessagesAction(final Parcel in) { 617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(in); 618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final Parcelable.Creator<SyncMessagesAction> CREATOR 621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd = new Parcelable.Creator<SyncMessagesAction>() { 622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SyncMessagesAction createFromParcel(final Parcel in) { 624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new SyncMessagesAction(in); 625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SyncMessagesAction[] newArray(final int size) { 629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new SyncMessagesAction[size]; 630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void writeToParcel(final Parcel parcel, final int flags) { 635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd writeActionToParcel(parcel, flags); 636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 638