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