blob: d3baf70d189c5770ea2473389af7af261b7ffab4 [file] [log] [blame]
Dianne Hackborn231cc602009-04-27 17:10:36 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017package android.content;
18
Dianne Hackborn231cc602009-04-27 17:10:36 -070019import com.android.internal.os.AtomicFile;
20import com.android.internal.util.ArrayUtils;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080021import com.android.internal.util.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022
Jason parksa3cdaa52011-01-13 14:15:43 -060023import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25import org.xmlpull.v1.XmlSerializer;
26
Fred Quintanad9d2f112009-04-23 13:36:27 -070027import android.accounts.Account;
Amith Yamasanif29f2362012-04-05 18:29:52 -070028import android.accounts.AccountAndUser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.database.sqlite.SQLiteDatabase;
Dianne Hackborn231cc602009-04-27 17:10:36 -070031import android.database.sqlite.SQLiteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.database.sqlite.SQLiteQueryBuilder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070033import android.os.Bundle;
34import android.os.Environment;
35import android.os.Handler;
36import android.os.Message;
37import android.os.Parcel;
38import android.os.RemoteCallbackList;
39import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.Log;
Dianne Hackborn231cc602009-04-27 17:10:36 -070041import android.util.SparseArray;
42import android.util.Xml;
Jason parksa3cdaa52011-01-13 14:15:43 -060043import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Dianne Hackborn231cc602009-04-27 17:10:36 -070045import java.io.File;
46import java.io.FileInputStream;
47import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import java.util.ArrayList;
Dianne Hackborn231cc602009-04-27 17:10:36 -070049import java.util.Calendar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070051import java.util.Iterator;
Jason parks1125d782011-01-12 09:47:26 -060052import java.util.TimeZone;
Jason parksa3cdaa52011-01-13 14:15:43 -060053import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
55/**
Dianne Hackborn231cc602009-04-27 17:10:36 -070056 * Singleton that tracks the sync data and overall sync
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 * history on the device.
Costin Manolache360e4542009-09-04 13:36:04 -070058 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 * @hide
60 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070061public class SyncStorageEngine extends Handler {
Amith Yamasani04e0d262012-02-14 11:50:53 -080062
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 private static final String TAG = "SyncManager";
Dianne Hackborn231cc602009-04-27 17:10:36 -070064 private static final boolean DEBUG_FILE = false;
Costin Manolache360e4542009-09-04 13:36:04 -070065
Amith Yamasani04e0d262012-02-14 11:50:53 -080066 private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId";
67 private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles";
68 private static final String XML_ATTR_ENABLED = "enabled";
69 private static final String XML_ATTR_USER = "user";
70 private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
71
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080072 private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
73
Dianne Hackborn231cc602009-04-27 17:10:36 -070074 // @VisibleForTesting
75 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
Dianne Hackborn231cc602009-04-27 17:10:36 -070077 /** Enum value for a sync start event. */
78 public static final int EVENT_START = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Dianne Hackborn231cc602009-04-27 17:10:36 -070080 /** Enum value for a sync stop event. */
81 public static final int EVENT_STOP = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Dianne Hackborn231cc602009-04-27 17:10:36 -070083 // TODO: i18n -- grab these out of resources.
84 /** String names for the sync event types. */
85 public static final String[] EVENTS = { "START", "STOP" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Dianne Hackborn231cc602009-04-27 17:10:36 -070087 /** Enum value for a server-initiated sync. */
88 public static final int SOURCE_SERVER = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
Dianne Hackborn231cc602009-04-27 17:10:36 -070090 /** Enum value for a local-initiated sync. */
91 public static final int SOURCE_LOCAL = 1;
92 /**
93 * Enum value for a poll-based sync (e.g., upon connection to
94 * network)
95 */
96 public static final int SOURCE_POLL = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
Dianne Hackborn231cc602009-04-27 17:10:36 -070098 /** Enum value for a user-initiated sync. */
99 public static final int SOURCE_USER = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800101 /** Enum value for a periodic sync. */
102 public static final int SOURCE_PERIODIC = 4;
103
Fred Quintana307da1a2010-01-21 14:24:20 -0800104 public static final long NOT_IN_BACKOFF_MODE = -1;
105
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700106 public static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
Fred Quintanaac9385e2009-06-22 18:00:59 -0700107 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
108
Dianne Hackborn231cc602009-04-27 17:10:36 -0700109 // TODO: i18n -- grab these out of resources.
110 /** String names for the sync source types. */
111 public static final String[] SOURCES = { "SERVER",
112 "LOCAL",
113 "POLL",
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800114 "USER",
115 "PERIODIC" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
Dianne Hackborn231cc602009-04-27 17:10:36 -0700117 // The MESG column will contain one of these or one of the Error types.
118 public static final String MESG_SUCCESS = "success";
119 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700121 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700122
Dianne Hackborn231cc602009-04-27 17:10:36 -0700123 private static final int MSG_WRITE_STATUS = 1;
124 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700125
Dianne Hackborn231cc602009-04-27 17:10:36 -0700126 private static final int MSG_WRITE_STATISTICS = 2;
127 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700128
129 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700130
Fred Quintanac2e46912010-03-15 16:10:44 -0700131 // the version of the accounts xml file format
Fred Quintanafb084402010-03-23 17:57:03 -0700132 private static final int ACCOUNTS_VERSION = 2;
133
134 private static HashMap<String, String> sAuthorityRenames;
135
136 static {
137 sAuthorityRenames = new HashMap<String, String>();
138 sAuthorityRenames.put("contacts", "com.android.contacts");
139 sAuthorityRenames.put("calendar", "com.android.calendar");
140 }
Fred Quintanac2e46912010-03-15 16:10:44 -0700141
Dianne Hackborn231cc602009-04-27 17:10:36 -0700142 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700143 final Account account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800144 final int userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700145 final int syncSource;
146 final String authority;
147 final Bundle extras; // note: read-only.
Fred Quintana307da1a2010-01-21 14:24:20 -0800148 final boolean expedited;
Costin Manolache360e4542009-09-04 13:36:04 -0700149
Dianne Hackborn231cc602009-04-27 17:10:36 -0700150 int authorityId;
151 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700152
Amith Yamasani04e0d262012-02-14 11:50:53 -0800153 PendingOperation(Account account, int userId, int source,
Fred Quintana307da1a2010-01-21 14:24:20 -0800154 String authority, Bundle extras, boolean expedited) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700155 this.account = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800156 this.userId = userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700157 this.syncSource = source;
158 this.authority = authority;
159 this.extras = extras != null ? new Bundle(extras) : extras;
Fred Quintana307da1a2010-01-21 14:24:20 -0800160 this.expedited = expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700161 this.authorityId = -1;
162 }
163
164 PendingOperation(PendingOperation other) {
165 this.account = other.account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800166 this.userId = other.userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700167 this.syncSource = other.syncSource;
168 this.authority = other.authority;
169 this.extras = other.extras;
170 this.authorityId = other.authorityId;
Fred Quintana307da1a2010-01-21 14:24:20 -0800171 this.expedited = other.expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 }
Costin Manolache360e4542009-09-04 13:36:04 -0700174
Dianne Hackborn231cc602009-04-27 17:10:36 -0700175 static class AccountInfo {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800176 final AccountAndUser accountAndUser;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700177 final HashMap<String, AuthorityInfo> authorities =
178 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700179
Amith Yamasani04e0d262012-02-14 11:50:53 -0800180 AccountInfo(AccountAndUser accountAndUser) {
181 this.accountAndUser = accountAndUser;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700182 }
183 }
Costin Manolache360e4542009-09-04 13:36:04 -0700184
Dianne Hackborn231cc602009-04-27 17:10:36 -0700185 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700186 final Account account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800187 final int userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700188 final String authority;
189 final int ident;
190 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700191 int syncable;
Fred Quintana307da1a2010-01-21 14:24:20 -0800192 long backoffTime;
193 long backoffDelay;
194 long delayUntil;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800195 final ArrayList<Pair<Bundle, Long>> periodicSyncs;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700196
Amith Yamasani04e0d262012-02-14 11:50:53 -0800197 AuthorityInfo(Account account, int userId, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700198 this.account = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800199 this.userId = userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700200 this.authority = authority;
201 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700202 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700203 syncable = -1; // default to "unknown"
Fred Quintana307da1a2010-01-21 14:24:20 -0800204 backoffTime = -1; // if < 0 then we aren't in backoff mode
205 backoffDelay = -1; // if < 0 then we aren't in backoff mode
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800206 periodicSyncs = new ArrayList<Pair<Bundle, Long>>();
207 periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700208 }
209 }
Costin Manolache360e4542009-09-04 13:36:04 -0700210
Dianne Hackborn231cc602009-04-27 17:10:36 -0700211 public static class SyncHistoryItem {
212 int authorityId;
213 int historyId;
214 long eventTime;
215 long elapsedTime;
216 int source;
217 int event;
218 long upstreamActivity;
219 long downstreamActivity;
220 String mesg;
221 }
Costin Manolache360e4542009-09-04 13:36:04 -0700222
Dianne Hackborn231cc602009-04-27 17:10:36 -0700223 public static class DayStats {
224 public final int day;
225 public int successCount;
226 public long successTime;
227 public int failureCount;
228 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700229
Dianne Hackborn231cc602009-04-27 17:10:36 -0700230 public DayStats(int day) {
231 this.day = day;
232 }
233 }
Costin Manolache360e4542009-09-04 13:36:04 -0700234
Amith Yamasani04e0d262012-02-14 11:50:53 -0800235 interface OnSyncRequestListener {
236 /**
237 * Called when a sync is needed on an account(s) due to some change in state.
238 * @param account
239 * @param userId
240 * @param authority
241 * @param extras
242 */
243 public void onSyncRequest(Account account, int userId, String authority, Bundle extras);
244 }
245
Dianne Hackborn231cc602009-04-27 17:10:36 -0700246 // Primary list of all syncable authorities. Also our global lock.
247 private final SparseArray<AuthorityInfo> mAuthorities =
248 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700249
Amith Yamasani04e0d262012-02-14 11:50:53 -0800250 private final HashMap<AccountAndUser, AccountInfo> mAccounts
251 = new HashMap<AccountAndUser, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252
Dianne Hackborn231cc602009-04-27 17:10:36 -0700253 private final ArrayList<PendingOperation> mPendingOperations =
254 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700255
Amith Yamasani04e0d262012-02-14 11:50:53 -0800256 private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs
257 = new SparseArray<ArrayList<SyncInfo>>();
Costin Manolache360e4542009-09-04 13:36:04 -0700258
Dianne Hackborn231cc602009-04-27 17:10:36 -0700259 private final SparseArray<SyncStatusInfo> mSyncStatus =
260 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700261
Dianne Hackborn231cc602009-04-27 17:10:36 -0700262 private final ArrayList<SyncHistoryItem> mSyncHistory =
263 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700264
Dianne Hackborn231cc602009-04-27 17:10:36 -0700265 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
266 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700267
Fred Quintana77c560f2010-03-29 22:20:26 -0700268 private int mNextAuthorityId = 0;
269
Dianne Hackborn231cc602009-04-27 17:10:36 -0700270 // We keep 4 weeks of stats.
271 private final DayStats[] mDayStats = new DayStats[7*4];
272 private final Calendar mCal;
273 private int mYear;
274 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700275
Dianne Hackborn231cc602009-04-27 17:10:36 -0700276 private final Context mContext;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800277
Dianne Hackborn231cc602009-04-27 17:10:36 -0700278 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700279
Dianne Hackborn231cc602009-04-27 17:10:36 -0700280 /**
281 * This file contains the core engine state: all accounts and the
282 * settings for them. It must never be lost, and should be changed
283 * infrequently, so it is stored as an XML file.
284 */
285 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700286
Dianne Hackborn231cc602009-04-27 17:10:36 -0700287 /**
288 * This file contains the current sync status. We would like to retain
289 * it across boots, but its loss is not the end of the world, so we store
290 * this information as binary data.
291 */
292 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700293
Dianne Hackborn231cc602009-04-27 17:10:36 -0700294 /**
295 * This file contains sync statistics. This is purely debugging information
296 * so is written infrequently and can be thrown away at any time.
297 */
298 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700299
Dianne Hackborn231cc602009-04-27 17:10:36 -0700300 /**
301 * This file contains the pending sync operations. It is a binary file,
302 * which must be updated every time an operation is added or removed,
303 * so we have special handling of it.
304 */
305 private final AtomicFile mPendingFile;
306 private static final int PENDING_FINISH_TO_WRITE = 4;
307 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700308
Dianne Hackborn231cc602009-04-27 17:10:36 -0700309 private int mNextHistoryId = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800310 private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
311
312 private OnSyncRequestListener mSyncRequestListener;
Costin Manolache360e4542009-09-04 13:36:04 -0700313
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800314 private SyncStorageEngine(Context context, File dataDir) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700317
Dianne Hackborn231cc602009-04-27 17:10:36 -0700318 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700319
Dianne Hackborn231cc602009-04-27 17:10:36 -0700320 File systemDir = new File(dataDir, "system");
321 File syncDir = new File(systemDir, "sync");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800322 syncDir.mkdirs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700323 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
324 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
325 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
326 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700327
Dianne Hackborn231cc602009-04-27 17:10:36 -0700328 readAccountInfoLocked();
329 readStatusLocked();
330 readPendingOperationsLocked();
331 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -0700332 readAndDeleteLegacyAccountInfoLocked();
333 writeAccountInfoLocked();
334 writeStatusLocked();
335 writePendingOperationsLocked();
336 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
338
339 public static SyncStorageEngine newTestInstance(Context context) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800340 return new SyncStorageEngine(context, context.getFilesDir());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342
343 public static void init(Context context) {
344 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800345 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800347 // This call will return the correct directory whether Encrypted File Systems is
348 // enabled or not.
Jason parksa3cdaa52011-01-13 14:15:43 -0600349 File dataDir = Environment.getSecureDataDirectory();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800350 sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352
353 public static SyncStorageEngine getSingleton() {
354 if (sSyncStorageEngine == null) {
355 throw new IllegalStateException("not initialized");
356 }
357 return sSyncStorageEngine;
358 }
359
Amith Yamasani04e0d262012-02-14 11:50:53 -0800360 protected void setOnSyncRequestListener(OnSyncRequestListener listener) {
361 if (mSyncRequestListener == null) {
362 mSyncRequestListener = listener;
363 }
364 }
365
Dianne Hackborn231cc602009-04-27 17:10:36 -0700366 @Override public void handleMessage(Message msg) {
367 if (msg.what == MSG_WRITE_STATUS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700368 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700369 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700370 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700371 } else if (msg.what == MSG_WRITE_STATISTICS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700372 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700373 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 }
376 }
Costin Manolache360e4542009-09-04 13:36:04 -0700377
Dianne Hackborn231cc602009-04-27 17:10:36 -0700378 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
379 synchronized (mAuthorities) {
380 mChangeListeners.register(callback, mask);
381 }
382 }
Costin Manolache360e4542009-09-04 13:36:04 -0700383
Dianne Hackborn231cc602009-04-27 17:10:36 -0700384 public void removeStatusChangeListener(ISyncStatusObserver callback) {
385 synchronized (mAuthorities) {
386 mChangeListeners.unregister(callback);
387 }
388 }
Costin Manolache360e4542009-09-04 13:36:04 -0700389
Dianne Hackborn231cc602009-04-27 17:10:36 -0700390 private void reportChange(int which) {
391 ArrayList<ISyncStatusObserver> reports = null;
392 synchronized (mAuthorities) {
393 int i = mChangeListeners.beginBroadcast();
394 while (i > 0) {
395 i--;
396 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
397 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 continue;
399 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700400 if (reports == null) {
401 reports = new ArrayList<ISyncStatusObserver>(i);
402 }
403 reports.add(mChangeListeners.getBroadcastItem(i));
404 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700405 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700406 }
Costin Manolache360e4542009-09-04 13:36:04 -0700407
Fred Quintana77c560f2010-03-29 22:20:26 -0700408 if (Log.isLoggable(TAG, Log.VERBOSE)) {
409 Log.v(TAG, "reportChange " + which + " to: " + reports);
410 }
Costin Manolache360e4542009-09-04 13:36:04 -0700411
Dianne Hackborn231cc602009-04-27 17:10:36 -0700412 if (reports != null) {
413 int i = reports.size();
414 while (i > 0) {
415 i--;
416 try {
417 reports.get(i).onStatusChanged(which);
418 } catch (RemoteException e) {
419 // The remote callback list will take care of this for us.
420 }
421 }
422 }
423 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700424
Amith Yamasani04e0d262012-02-14 11:50:53 -0800425 public boolean getSyncAutomatically(Account account, int userId, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700426 synchronized (mAuthorities) {
427 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800428 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700429 "getSyncAutomatically");
430 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700431 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700432
Dianne Hackborn231cc602009-04-27 17:10:36 -0700433 int i = mAuthorities.size();
434 while (i > 0) {
435 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700436 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700437 if (authority.authority.equals(providerName)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800438 && authority.userId == userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700439 && authority.enabled) {
440 return true;
441 }
442 }
443 return false;
444 }
445 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446
Amith Yamasani04e0d262012-02-14 11:50:53 -0800447 public void setSyncAutomatically(Account account, int userId, String providerName,
448 boolean sync) {
449 Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
450 + ", user " + userId + " -> " + sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700451 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800452 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
453 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700454 if (authority.enabled == sync) {
455 Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
456 return;
457 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700458 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700459 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700461
Fred Quintana77c560f2010-03-29 22:20:26 -0700462 if (sync) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800463 requestSync(account, userId, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700464 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700465 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
467
Amith Yamasani04e0d262012-02-14 11:50:53 -0800468 public int getIsSyncable(Account account, int userId, String providerName) {
Fred Quintana5e787c42009-08-16 23:13:53 -0700469 synchronized (mAuthorities) {
470 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800471 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintana5e787c42009-08-16 23:13:53 -0700472 "getIsSyncable");
473 if (authority == null) {
474 return -1;
475 }
476 return authority.syncable;
477 }
478
479 int i = mAuthorities.size();
480 while (i > 0) {
481 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700482 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700483 if (authority.authority.equals(providerName)) {
484 return authority.syncable;
485 }
486 }
487 return -1;
488 }
489 }
490
Amith Yamasani04e0d262012-02-14 11:50:53 -0800491 public void setIsSyncable(Account account, int userId, String providerName, int syncable) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700492 if (syncable > 1) {
493 syncable = 1;
494 } else if (syncable < -1) {
495 syncable = -1;
496 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800497 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName
498 + ", user " + userId + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700499 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800500 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
501 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700502 if (authority.syncable == syncable) {
503 Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
504 return;
505 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700506 authority.syncable = syncable;
507 writeAccountInfoLocked();
508 }
509
Fred Quintana77c560f2010-03-29 22:20:26 -0700510 if (syncable > 0) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800511 requestSync(account, userId, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700512 }
513 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
514 }
515
Amith Yamasani04e0d262012-02-14 11:50:53 -0800516 public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800517 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800518 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
519 "getBackoff");
Fred Quintana307da1a2010-01-21 14:24:20 -0800520 if (authority == null || authority.backoffTime < 0) {
521 return null;
522 }
523 return Pair.create(authority.backoffTime, authority.backoffDelay);
524 }
525 }
526
Amith Yamasani04e0d262012-02-14 11:50:53 -0800527 public void setBackoff(Account account, int userId, String providerName,
Fred Quintana307da1a2010-01-21 14:24:20 -0800528 long nextSyncTime, long nextDelay) {
529 if (Log.isLoggable(TAG, Log.VERBOSE)) {
530 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800531 + ", user " + userId
Fred Quintana307da1a2010-01-21 14:24:20 -0800532 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
533 }
534 boolean changed = false;
535 synchronized (mAuthorities) {
536 if (account == null || providerName == null) {
537 for (AccountInfo accountInfo : mAccounts.values()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800538 if (account != null && !account.equals(accountInfo.accountAndUser.account)
539 && userId != accountInfo.accountAndUser.userId) {
540 continue;
541 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800542 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
543 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
544 continue;
545 }
546 if (authorityInfo.backoffTime != nextSyncTime
547 || authorityInfo.backoffDelay != nextDelay) {
548 authorityInfo.backoffTime = nextSyncTime;
549 authorityInfo.backoffDelay = nextDelay;
550 changed = true;
551 }
552 }
553 }
554 } else {
555 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800556 getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */,
557 true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800558 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
559 return;
560 }
561 authority.backoffTime = nextSyncTime;
562 authority.backoffDelay = nextDelay;
563 changed = true;
564 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800565 }
566
567 if (changed) {
568 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
569 }
570 }
571
Alon Alberted1d2532011-02-15 14:02:14 -0800572 public void clearAllBackoffs(SyncQueue syncQueue) {
Alon Albert744e310f2010-12-14 11:37:20 -0800573 boolean changed = false;
574 synchronized (mAuthorities) {
575 for (AccountInfo accountInfo : mAccounts.values()) {
576 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
577 if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
578 || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
579 if (Log.isLoggable(TAG, Log.VERBOSE)) {
580 Log.v(TAG, "clearAllBackoffs:"
581 + " authority:" + authorityInfo.authority
Amith Yamasani04e0d262012-02-14 11:50:53 -0800582 + " account:" + accountInfo.accountAndUser.account.name
583 + " user:" + accountInfo.accountAndUser.userId
Alon Albert744e310f2010-12-14 11:37:20 -0800584 + " backoffTime was: " + authorityInfo.backoffTime
585 + " backoffDelay was: " + authorityInfo.backoffDelay);
586 }
587 authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
588 authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800589 syncQueue.onBackoffChanged(accountInfo.accountAndUser.account,
590 accountInfo.accountAndUser.userId, authorityInfo.authority, 0);
Alon Albert744e310f2010-12-14 11:37:20 -0800591 changed = true;
592 }
593 }
594 }
595 }
596
597 if (changed) {
598 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
599 }
600 }
601
Amith Yamasani04e0d262012-02-14 11:50:53 -0800602 public void setDelayUntilTime(Account account, int userId, String providerName,
603 long delayUntil) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800604 if (Log.isLoggable(TAG, Log.VERBOSE)) {
605 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800606 + ", user " + userId + " -> delayUntil " + delayUntil);
Fred Quintana307da1a2010-01-21 14:24:20 -0800607 }
608 synchronized (mAuthorities) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800609 AuthorityInfo authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800610 account, userId, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800611 if (authority.delayUntil == delayUntil) {
612 return;
613 }
614 authority.delayUntil = delayUntil;
Fred Quintana307da1a2010-01-21 14:24:20 -0800615 }
616
617 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
618 }
619
Amith Yamasani04e0d262012-02-14 11:50:53 -0800620 public long getDelayUntilTime(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800621 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800622 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
623 "getDelayUntil");
Fred Quintana307da1a2010-01-21 14:24:20 -0800624 if (authority == null) {
625 return 0;
626 }
627 return authority.delayUntil;
628 }
629 }
630
Amith Yamasani04e0d262012-02-14 11:50:53 -0800631 private void updateOrRemovePeriodicSync(Account account, int userId, String providerName,
632 Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800633 long period, boolean add) {
634 if (period <= 0) {
635 period = 0;
636 }
637 if (extras == null) {
638 extras = new Bundle();
639 }
640 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800641 Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId
642 + ", provider " + providerName
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800643 + " -> period " + period + ", extras " + extras);
644 }
645 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700646 try {
647 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800648 getOrCreateAuthorityLocked(account, userId, providerName, -1, false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700649 if (add) {
650 // add this periodic sync if one with the same extras doesn't already
651 // exist in the periodicSyncs array
652 boolean alreadyPresent = false;
653 for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
654 Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
655 final Bundle existingExtras = syncInfo.first;
656 if (equals(existingExtras, extras)) {
657 if (syncInfo.second == period) {
658 return;
659 }
660 authority.periodicSyncs.set(i, Pair.create(extras, period));
661 alreadyPresent = true;
662 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800663 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700664 }
665 // if we added an entry to the periodicSyncs array also add an entry to
666 // the periodic syncs status to correspond to it
667 if (!alreadyPresent) {
668 authority.periodicSyncs.add(Pair.create(extras, period));
669 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
670 status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
671 }
672 } else {
673 // remove any periodic syncs that match the authority and extras
674 SyncStatusInfo status = mSyncStatus.get(authority.ident);
675 boolean changed = false;
676 Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
677 int i = 0;
678 while (iterator.hasNext()) {
679 Pair<Bundle, Long> syncInfo = iterator.next();
680 if (equals(syncInfo.first, extras)) {
681 iterator.remove();
682 changed = true;
683 // if we removed an entry from the periodicSyncs array also
684 // remove the corresponding entry from the status
685 if (status != null) {
686 status.removePeriodicSyncTime(i);
687 }
688 } else {
689 i++;
690 }
691 }
692 if (!changed) {
693 return;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800694 }
695 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700696 } finally {
697 writeAccountInfoLocked();
698 writeStatusLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800699 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800700 }
701
702 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
703 }
704
Amith Yamasani04e0d262012-02-14 11:50:53 -0800705 public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800706 long pollFrequency) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800707 updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency,
708 true /* add */);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800709 }
710
Amith Yamasani04e0d262012-02-14 11:50:53 -0800711 public void removePeriodicSync(Account account, int userId, String providerName,
712 Bundle extras) {
713 updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800714 false /* remove */);
715 }
716
Amith Yamasani04e0d262012-02-14 11:50:53 -0800717 public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800718 ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
719 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800720 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
721 "getPeriodicSyncs");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800722 if (authority != null) {
723 for (Pair<Bundle, Long> item : authority.periodicSyncs) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800724 syncs.add(new PeriodicSync(account, providerName, item.first,
725 item.second));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800726 }
727 }
728 }
729 return syncs;
730 }
731
Amith Yamasani04e0d262012-02-14 11:50:53 -0800732 public void setMasterSyncAutomatically(boolean flag, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700733 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800734 Boolean auto = mMasterSyncAutomatically.get(userId);
735 if (auto != null && (boolean) auto == flag) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700736 return;
737 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800738 mMasterSyncAutomatically.put(userId, flag);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700739 writeAccountInfoLocked();
740 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700741 if (flag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800742 requestSync(null, userId, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700743 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700744 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
745 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747
Amith Yamasani04e0d262012-02-14 11:50:53 -0800748 public boolean getMasterSyncAutomatically(int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700749 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800750 Boolean auto = mMasterSyncAutomatically.get(userId);
751 return auto == null ? true : auto;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700752 }
753 }
Costin Manolache360e4542009-09-04 13:36:04 -0700754
Amith Yamasani04e0d262012-02-14 11:50:53 -0800755 public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700756 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800757 return getOrCreateAuthorityLocked(account, userId, authority,
Fred Quintana1bbcd102010-02-10 10:04:33 -0800758 -1 /* assign a new identifier if creating a new authority */,
759 true /* write to storage if this results in a change */);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700760 }
761 }
Costin Manolache360e4542009-09-04 13:36:04 -0700762
Amith Yamasani04e0d262012-02-14 11:50:53 -0800763 public void removeAuthority(Account account, int userId, String authority) {
Fred Quintana7620f1a2010-03-16 15:58:44 -0700764 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800765 removeAuthorityLocked(account, userId, authority, true /* doWrite */);
Fred Quintana7620f1a2010-03-16 15:58:44 -0700766 }
767 }
768
Dianne Hackborn231cc602009-04-27 17:10:36 -0700769 public AuthorityInfo getAuthority(int authorityId) {
770 synchronized (mAuthorities) {
771 return mAuthorities.get(authorityId);
772 }
773 }
Costin Manolache360e4542009-09-04 13:36:04 -0700774
Dianne Hackborn231cc602009-04-27 17:10:36 -0700775 /**
776 * Returns true if there is currently a sync operation for the given
Fred Quintana918339a2010-10-05 14:00:39 -0700777 * account or authority actively being processed.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700778 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800779 public boolean isSyncActive(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700780 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800781 for (SyncInfo syncInfo : getCurrentSyncs(userId)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700782 AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700783 if (ainfo != null && ainfo.account.equals(account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800784 && ainfo.authority.equals(authority)
785 && ainfo.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700786 return true;
787 }
788 }
789 }
Costin Manolache360e4542009-09-04 13:36:04 -0700790
Dianne Hackborn231cc602009-04-27 17:10:36 -0700791 return false;
792 }
Costin Manolache360e4542009-09-04 13:36:04 -0700793
Dianne Hackborn231cc602009-04-27 17:10:36 -0700794 public PendingOperation insertIntoPending(PendingOperation op) {
795 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700796 if (Log.isLoggable(TAG, Log.VERBOSE)) {
797 Log.v(TAG, "insertIntoPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800798 + " user=" + op.userId
799 + " auth=" + op.authority
800 + " src=" + op.syncSource
801 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700802 }
Costin Manolache360e4542009-09-04 13:36:04 -0700803
Amith Yamasani04e0d262012-02-14 11:50:53 -0800804 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700805 op.authority,
806 -1 /* desired identifier */,
807 true /* write accounts to storage */);
808 if (authority == null) {
809 return null;
810 }
Costin Manolache360e4542009-09-04 13:36:04 -0700811
Dianne Hackborn231cc602009-04-27 17:10:36 -0700812 op = new PendingOperation(op);
813 op.authorityId = authority.ident;
814 mPendingOperations.add(op);
815 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700816
Dianne Hackborn231cc602009-04-27 17:10:36 -0700817 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
818 status.pending = true;
819 }
Costin Manolache360e4542009-09-04 13:36:04 -0700820
Fred Quintanaac9385e2009-06-22 18:00:59 -0700821 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700822 return op;
823 }
824
825 public boolean deleteFromPending(PendingOperation op) {
826 boolean res = false;
827 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700828 if (Log.isLoggable(TAG, Log.VERBOSE)) {
829 Log.v(TAG, "deleteFromPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800830 + " user=" + op.userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700831 + " auth=" + op.authority
832 + " src=" + op.syncSource
833 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700834 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700835 if (mPendingOperations.remove(op)) {
836 if (mPendingOperations.size() == 0
837 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
838 writePendingOperationsLocked();
839 mNumPendingFinished = 0;
840 } else {
841 mNumPendingFinished++;
842 }
Costin Manolache360e4542009-09-04 13:36:04 -0700843
Amith Yamasani04e0d262012-02-14 11:50:53 -0800844 AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700845 "deleteFromPending");
846 if (authority != null) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700847 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700848 final int N = mPendingOperations.size();
849 boolean morePending = false;
850 for (int i=0; i<N; i++) {
851 PendingOperation cur = mPendingOperations.get(i);
852 if (cur.account.equals(op.account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800853 && cur.authority.equals(op.authority)
854 && cur.userId == op.userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700855 morePending = true;
856 break;
857 }
858 }
Costin Manolache360e4542009-09-04 13:36:04 -0700859
Dianne Hackborn231cc602009-04-27 17:10:36 -0700860 if (!morePending) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700861 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700862 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
863 status.pending = false;
864 }
865 }
Costin Manolache360e4542009-09-04 13:36:04 -0700866
Dianne Hackborn231cc602009-04-27 17:10:36 -0700867 res = true;
868 }
869 }
Costin Manolache360e4542009-09-04 13:36:04 -0700870
Fred Quintanaac9385e2009-06-22 18:00:59 -0700871 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700872 return res;
873 }
874
Dianne Hackborn231cc602009-04-27 17:10:36 -0700875 /**
876 * Return a copy of the current array of pending operations. The
877 * PendingOperation objects are the real objects stored inside, so that
878 * they can be used with deleteFromPending().
879 */
880 public ArrayList<PendingOperation> getPendingOperations() {
881 synchronized (mAuthorities) {
882 return new ArrayList<PendingOperation>(mPendingOperations);
883 }
884 }
Costin Manolache360e4542009-09-04 13:36:04 -0700885
Dianne Hackborn231cc602009-04-27 17:10:36 -0700886 /**
887 * Return the number of currently pending operations.
888 */
889 public int getPendingOperationCount() {
890 synchronized (mAuthorities) {
891 return mPendingOperations.size();
892 }
893 }
Costin Manolache360e4542009-09-04 13:36:04 -0700894
Dianne Hackborn231cc602009-04-27 17:10:36 -0700895 /**
896 * Called when the set of account has changed, given the new array of
897 * active accounts.
898 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800899 public void doDatabaseCleanup(Account[] accounts, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700900 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700901 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts...");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700902 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
903 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
904 while (accIt.hasNext()) {
905 AccountInfo acc = accIt.next();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800906 if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
907 && acc.accountAndUser.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700908 // This account no longer exists...
Fred Quintana77c560f2010-03-29 22:20:26 -0700909 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800910 Log.w(TAG, "Account removed: " + acc.accountAndUser);
Fred Quintana77c560f2010-03-29 22:20:26 -0700911 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700912 for (AuthorityInfo auth : acc.authorities.values()) {
913 removing.put(auth.ident, auth);
914 }
915 accIt.remove();
916 }
917 }
Costin Manolache360e4542009-09-04 13:36:04 -0700918
Dianne Hackborn231cc602009-04-27 17:10:36 -0700919 // Clean out all data structures.
920 int i = removing.size();
921 if (i > 0) {
922 while (i > 0) {
923 i--;
924 int ident = removing.keyAt(i);
925 mAuthorities.remove(ident);
926 int j = mSyncStatus.size();
927 while (j > 0) {
928 j--;
929 if (mSyncStatus.keyAt(j) == ident) {
930 mSyncStatus.remove(mSyncStatus.keyAt(j));
931 }
932 }
933 j = mSyncHistory.size();
934 while (j > 0) {
935 j--;
936 if (mSyncHistory.get(j).authorityId == ident) {
937 mSyncHistory.remove(j);
938 }
939 }
940 }
941 writeAccountInfoLocked();
942 writeStatusLocked();
943 writePendingOperationsLocked();
944 writeStatisticsLocked();
945 }
946 }
947 }
948
949 /**
Fred Quintana918339a2010-10-05 14:00:39 -0700950 * Called when a sync is starting. Supply a valid ActiveSyncContext with information
951 * about the sync.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700952 */
Fred Quintana918339a2010-10-05 14:00:39 -0700953 public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
954 final SyncInfo syncInfo;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700955 synchronized (mAuthorities) {
Fred Quintana918339a2010-10-05 14:00:39 -0700956 if (Log.isLoggable(TAG, Log.VERBOSE)) {
957 Log.v(TAG, "setActiveSync: account="
958 + activeSyncContext.mSyncOperation.account
959 + " auth=" + activeSyncContext.mSyncOperation.authority
960 + " src=" + activeSyncContext.mSyncOperation.syncSource
961 + " extras=" + activeSyncContext.mSyncOperation.extras);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700962 }
Fred Quintana918339a2010-10-05 14:00:39 -0700963 AuthorityInfo authority = getOrCreateAuthorityLocked(
964 activeSyncContext.mSyncOperation.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800965 activeSyncContext.mSyncOperation.userId,
Fred Quintana918339a2010-10-05 14:00:39 -0700966 activeSyncContext.mSyncOperation.authority,
967 -1 /* assign a new identifier if creating a new authority */,
968 true /* write to storage if this results in a change */);
969 syncInfo = new SyncInfo(authority.ident,
970 authority.account, authority.authority,
971 activeSyncContext.mStartTime);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800972 getCurrentSyncs(authority.userId).add(syncInfo);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700973 }
Costin Manolache360e4542009-09-04 13:36:04 -0700974
Fred Quintana918339a2010-10-05 14:00:39 -0700975 reportActiveChange();
976 return syncInfo;
977 }
978
979 /**
980 * Called to indicate that a previously active sync is no longer active.
981 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800982 public void removeActiveSync(SyncInfo syncInfo, int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -0700983 synchronized (mAuthorities) {
984 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800985 Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
986 + " user=" + userId
987 + " auth=" + syncInfo.authority);
Fred Quintana918339a2010-10-05 14:00:39 -0700988 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800989 getCurrentSyncs(userId).remove(syncInfo);
Fred Quintana918339a2010-10-05 14:00:39 -0700990 }
991
992 reportActiveChange();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700993 }
994
995 /**
996 * To allow others to send active change reports, to poke clients.
997 */
998 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700999 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001000 }
Costin Manolache360e4542009-09-04 13:36:04 -07001001
Dianne Hackborn231cc602009-04-27 17:10:36 -07001002 /**
1003 * Note that sync has started for the given account and authority.
1004 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001005 public long insertStartSyncEvent(Account accountName, int userId, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001006 long now, int source) {
1007 long id;
1008 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001009 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001010 Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId
Dianne Hackborn231cc602009-04-27 17:10:36 -07001011 + " auth=" + authorityName + " source=" + source);
Fred Quintana77c560f2010-03-29 22:20:26 -07001012 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001013 AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001014 "insertStartSyncEvent");
1015 if (authority == null) {
1016 return -1;
1017 }
1018 SyncHistoryItem item = new SyncHistoryItem();
1019 item.authorityId = authority.ident;
1020 item.historyId = mNextHistoryId++;
1021 if (mNextHistoryId < 0) mNextHistoryId = 0;
1022 item.eventTime = now;
1023 item.source = source;
1024 item.event = EVENT_START;
1025 mSyncHistory.add(0, item);
1026 while (mSyncHistory.size() > MAX_HISTORY) {
1027 mSyncHistory.remove(mSyncHistory.size()-1);
1028 }
1029 id = item.historyId;
Fred Quintana77c560f2010-03-29 22:20:26 -07001030 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001031 }
Costin Manolache360e4542009-09-04 13:36:04 -07001032
Fred Quintanaac9385e2009-06-22 18:00:59 -07001033 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001034 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 }
1036
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001037 public static boolean equals(Bundle b1, Bundle b2) {
1038 if (b1.size() != b2.size()) {
1039 return false;
1040 }
1041 if (b1.isEmpty()) {
1042 return true;
1043 }
1044 for (String key : b1.keySet()) {
1045 if (!b2.containsKey(key)) {
1046 return false;
1047 }
1048 if (!b1.get(key).equals(b2.get(key))) {
1049 return false;
1050 }
1051 }
1052 return true;
1053 }
1054
Fred Quintana77c560f2010-03-29 22:20:26 -07001055 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001057 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001058 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1059 Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
1060 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001061 SyncHistoryItem item = null;
1062 int i = mSyncHistory.size();
1063 while (i > 0) {
1064 i--;
1065 item = mSyncHistory.get(i);
1066 if (item.historyId == historyId) {
1067 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001069 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 }
Costin Manolache360e4542009-09-04 13:36:04 -07001071
Dianne Hackborn231cc602009-04-27 17:10:36 -07001072 if (item == null) {
1073 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
1074 return;
1075 }
Costin Manolache360e4542009-09-04 13:36:04 -07001076
Dianne Hackborn231cc602009-04-27 17:10:36 -07001077 item.elapsedTime = elapsedTime;
1078 item.event = EVENT_STOP;
1079 item.mesg = resultMessage;
1080 item.downstreamActivity = downstreamActivity;
1081 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -07001082
Dianne Hackborn231cc602009-04-27 17:10:36 -07001083 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -07001084
Dianne Hackborn231cc602009-04-27 17:10:36 -07001085 status.numSyncs++;
1086 status.totalElapsedTime += elapsedTime;
1087 switch (item.source) {
1088 case SOURCE_LOCAL:
1089 status.numSourceLocal++;
1090 break;
1091 case SOURCE_POLL:
1092 status.numSourcePoll++;
1093 break;
1094 case SOURCE_USER:
1095 status.numSourceUser++;
1096 break;
1097 case SOURCE_SERVER:
1098 status.numSourceServer++;
1099 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001100 case SOURCE_PERIODIC:
1101 status.numSourcePeriodic++;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001102 break;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001103 }
Costin Manolache360e4542009-09-04 13:36:04 -07001104
Dianne Hackborn231cc602009-04-27 17:10:36 -07001105 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -07001106 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001107 if (mDayStats[0] == null) {
1108 mDayStats[0] = new DayStats(day);
1109 } else if (day != mDayStats[0].day) {
1110 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
1111 mDayStats[0] = new DayStats(day);
1112 writeStatisticsNow = true;
1113 } else if (mDayStats[0] == null) {
1114 }
1115 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -07001116
Dianne Hackborn231cc602009-04-27 17:10:36 -07001117 final long lastSyncTime = (item.eventTime + elapsedTime);
1118 boolean writeStatusNow = false;
1119 if (MESG_SUCCESS.equals(resultMessage)) {
1120 // - if successful, update the successful columns
1121 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
1122 writeStatusNow = true;
1123 }
1124 status.lastSuccessTime = lastSyncTime;
1125 status.lastSuccessSource = item.source;
1126 status.lastFailureTime = 0;
1127 status.lastFailureSource = -1;
1128 status.lastFailureMesg = null;
1129 status.initialFailureTime = 0;
1130 ds.successCount++;
1131 ds.successTime += elapsedTime;
1132 } else if (!MESG_CANCELED.equals(resultMessage)) {
1133 if (status.lastFailureTime == 0) {
1134 writeStatusNow = true;
1135 }
1136 status.lastFailureTime = lastSyncTime;
1137 status.lastFailureSource = item.source;
1138 status.lastFailureMesg = resultMessage;
1139 if (status.initialFailureTime == 0) {
1140 status.initialFailureTime = lastSyncTime;
1141 }
1142 ds.failureCount++;
1143 ds.failureTime += elapsedTime;
1144 }
Costin Manolache360e4542009-09-04 13:36:04 -07001145
Dianne Hackborn231cc602009-04-27 17:10:36 -07001146 if (writeStatusNow) {
1147 writeStatusLocked();
1148 } else if (!hasMessages(MSG_WRITE_STATUS)) {
1149 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
1150 WRITE_STATUS_DELAY);
1151 }
1152 if (writeStatisticsNow) {
1153 writeStatisticsLocked();
1154 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
1155 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
1156 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -07001157 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001158 }
Costin Manolache360e4542009-09-04 13:36:04 -07001159
Fred Quintanaac9385e2009-06-22 18:00:59 -07001160 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001161 }
1162
1163 /**
Fred Quintana918339a2010-10-05 14:00:39 -07001164 * Return a list of the currently active syncs. Note that the returned items are the
1165 * real, live active sync objects, so be careful what you do with it.
1166 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001167 public List<SyncInfo> getCurrentSyncs(int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -07001168 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001169 ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId);
1170 if (syncs == null) {
1171 syncs = new ArrayList<SyncInfo>();
1172 mCurrentSyncs.put(userId, syncs);
1173 }
Amith Yamasani1b6ae002012-03-14 14:53:36 -07001174 return syncs;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001175 }
1176 }
Costin Manolache360e4542009-09-04 13:36:04 -07001177
Dianne Hackborn231cc602009-04-27 17:10:36 -07001178 /**
1179 * Return an array of the current sync status for all authorities. Note
1180 * that the objects inside the array are the real, live status objects,
1181 * so be careful what you do with them.
1182 */
1183 public ArrayList<SyncStatusInfo> getSyncStatus() {
1184 synchronized (mAuthorities) {
1185 final int N = mSyncStatus.size();
1186 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
1187 for (int i=0; i<N; i++) {
1188 ops.add(mSyncStatus.valueAt(i));
1189 }
1190 return ops;
1191 }
1192 }
Costin Manolache360e4542009-09-04 13:36:04 -07001193
Dianne Hackborn231cc602009-04-27 17:10:36 -07001194 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001195 * Return an array of the current authorities. Note
1196 * that the objects inside the array are the real, live objects,
1197 * so be careful what you do with them.
1198 */
1199 public ArrayList<AuthorityInfo> getAuthorities() {
1200 synchronized (mAuthorities) {
1201 final int N = mAuthorities.size();
1202 ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
1203 for (int i=0; i<N; i++) {
1204 infos.add(mAuthorities.valueAt(i));
1205 }
1206 return infos;
1207 }
1208 }
1209
1210 /**
Costin Manolacheb7520982009-09-02 18:03:05 -07001211 * Returns the status that matches the authority and account.
1212 *
1213 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -07001214 * @param authority the authority whose row should be selected
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001215 * @return the SyncStatusInfo for the authority
Dianne Hackborn231cc602009-04-27 17:10:36 -07001216 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001217 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId,
1218 String authority) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001219 if (account == null || authority == null) {
1220 throw new IllegalArgumentException();
1221 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001222 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001223 final int N = mSyncStatus.size();
1224 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001225 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001226 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -07001227
Amith Yamasani04e0d262012-02-14 11:50:53 -08001228 if (ainfo != null && ainfo.authority.equals(authority)
1229 && ainfo.userId == userId
1230 && account.equals(ainfo.account)) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001231 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001232 }
1233 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001234 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001235 }
1236 }
Costin Manolache360e4542009-09-04 13:36:04 -07001237
Dianne Hackborn231cc602009-04-27 17:10:36 -07001238 /**
1239 * Return true if the pending status is true of any matching authorities.
1240 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001241 public boolean isSyncPending(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001242 synchronized (mAuthorities) {
1243 final int N = mSyncStatus.size();
1244 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001245 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001246 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
1247 if (ainfo == null) {
1248 continue;
1249 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001250 if (userId != ainfo.userId) {
1251 continue;
1252 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001253 if (account != null && !ainfo.account.equals(account)) {
1254 continue;
1255 }
1256 if (ainfo.authority.equals(authority) && cur.pending) {
1257 return true;
1258 }
1259 }
1260 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261 }
1262 }
1263
1264 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001265 * Return an array of the current sync status for all authorities. Note
1266 * that the objects inside the array are the real, live status objects,
1267 * so be careful what you do with them.
1268 */
1269 public ArrayList<SyncHistoryItem> getSyncHistory() {
1270 synchronized (mAuthorities) {
1271 final int N = mSyncHistory.size();
1272 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1273 for (int i=0; i<N; i++) {
1274 items.add(mSyncHistory.get(i));
1275 }
1276 return items;
1277 }
1278 }
Costin Manolache360e4542009-09-04 13:36:04 -07001279
Dianne Hackborn231cc602009-04-27 17:10:36 -07001280 /**
1281 * Return an array of the current per-day statistics. Note
1282 * that the objects inside the array are the real, live status objects,
1283 * so be careful what you do with them.
1284 */
1285 public DayStats[] getDayStatistics() {
1286 synchronized (mAuthorities) {
1287 DayStats[] ds = new DayStats[mDayStats.length];
1288 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1289 return ds;
1290 }
1291 }
Costin Manolache360e4542009-09-04 13:36:04 -07001292
Dianne Hackborn55280a92009-05-07 15:53:46 -07001293 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001294 mCal.setTimeInMillis(System.currentTimeMillis());
1295 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1296 if (mYear != mCal.get(Calendar.YEAR)) {
1297 mYear = mCal.get(Calendar.YEAR);
1298 mCal.clear();
1299 mCal.set(Calendar.YEAR, mYear);
1300 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1301 }
1302 return dayOfYear + mYearInDays;
1303 }
Costin Manolache360e4542009-09-04 13:36:04 -07001304
Dianne Hackborn231cc602009-04-27 17:10:36 -07001305 /**
1306 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001307 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001308 * @param accountName The name of the account for the authority.
1309 * @param authorityName The name of the authority itself.
1310 * @param tag If non-null, this will be used in a log message if the
1311 * requested authority does not exist.
1312 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001313 private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001314 String tag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001315 AccountAndUser au = new AccountAndUser(accountName, userId);
1316 AccountInfo accountInfo = mAccounts.get(au);
1317 if (accountInfo == null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001318 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001319 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001320 Log.v(TAG, tag + ": unknown account " + au);
Fred Quintanab763ab22009-08-18 18:07:30 -07001321 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001322 }
1323 return null;
1324 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001325 AuthorityInfo authority = accountInfo.authorities.get(authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001326 if (authority == null) {
1327 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001328 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1329 Log.v(TAG, tag + ": unknown authority " + authorityName);
1330 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001331 }
1332 return null;
1333 }
Costin Manolache360e4542009-09-04 13:36:04 -07001334
Dianne Hackborn231cc602009-04-27 17:10:36 -07001335 return authority;
1336 }
Costin Manolache360e4542009-09-04 13:36:04 -07001337
Amith Yamasani04e0d262012-02-14 11:50:53 -08001338 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001339 String authorityName, int ident, boolean doWrite) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001340 AccountAndUser au = new AccountAndUser(accountName, userId);
1341 AccountInfo account = mAccounts.get(au);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001342 if (account == null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001343 account = new AccountInfo(au);
1344 mAccounts.put(au, account);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001345 }
1346 AuthorityInfo authority = account.authorities.get(authorityName);
1347 if (authority == null) {
1348 if (ident < 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001349 ident = mNextAuthorityId;
1350 mNextAuthorityId++;
1351 doWrite = true;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001352 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001353 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1354 Log.v(TAG, "created a new AuthorityInfo for " + accountName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001355 + ", user " + userId
1356 + ", provider " + authorityName);
Fred Quintana77c560f2010-03-29 22:20:26 -07001357 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001358 authority = new AuthorityInfo(accountName, userId, authorityName, ident);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001359 account.authorities.put(authorityName, authority);
1360 mAuthorities.put(ident, authority);
1361 if (doWrite) {
1362 writeAccountInfoLocked();
1363 }
1364 }
Costin Manolache360e4542009-09-04 13:36:04 -07001365
Dianne Hackborn231cc602009-04-27 17:10:36 -07001366 return authority;
1367 }
Costin Manolache360e4542009-09-04 13:36:04 -07001368
Amith Yamasani04e0d262012-02-14 11:50:53 -08001369 private void removeAuthorityLocked(Account account, int userId, String authorityName,
1370 boolean doWrite) {
1371 AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
Fred Quintana7620f1a2010-03-16 15:58:44 -07001372 if (accountInfo != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001373 final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
1374 if (authorityInfo != null) {
1375 mAuthorities.remove(authorityInfo.ident);
Fred Quintana77c560f2010-03-29 22:20:26 -07001376 if (doWrite) {
1377 writeAccountInfoLocked();
1378 }
Fred Quintana7620f1a2010-03-16 15:58:44 -07001379 }
1380 }
1381 }
1382
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001383 public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
1384 synchronized (mAuthorities) {
1385 return getOrCreateSyncStatusLocked(authority.ident);
1386 }
1387 }
1388
Dianne Hackborn231cc602009-04-27 17:10:36 -07001389 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1390 SyncStatusInfo status = mSyncStatus.get(authorityId);
1391 if (status == null) {
1392 status = new SyncStatusInfo(authorityId);
1393 mSyncStatus.put(authorityId, status);
1394 }
1395 return status;
1396 }
Costin Manolache360e4542009-09-04 13:36:04 -07001397
Dianne Hackborn55280a92009-05-07 15:53:46 -07001398 public void writeAllState() {
1399 synchronized (mAuthorities) {
1400 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001401
Dianne Hackborn55280a92009-05-07 15:53:46 -07001402 if (mNumPendingFinished > 0) {
1403 // Only write these if they are out of date.
1404 writePendingOperationsLocked();
1405 }
Costin Manolache360e4542009-09-04 13:36:04 -07001406
Dianne Hackborn55280a92009-05-07 15:53:46 -07001407 // Just always write these... they are likely out of date.
1408 writeStatusLocked();
1409 writeStatisticsLocked();
1410 }
1411 }
Costin Manolache360e4542009-09-04 13:36:04 -07001412
Dianne Hackborn231cc602009-04-27 17:10:36 -07001413 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001414 * public for testing
1415 */
1416 public void clearAndReadState() {
1417 synchronized (mAuthorities) {
1418 mAuthorities.clear();
1419 mAccounts.clear();
1420 mPendingOperations.clear();
1421 mSyncStatus.clear();
1422 mSyncHistory.clear();
1423
1424 readAccountInfoLocked();
1425 readStatusLocked();
1426 readPendingOperationsLocked();
1427 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -07001428 readAndDeleteLegacyAccountInfoLocked();
1429 writeAccountInfoLocked();
1430 writeStatusLocked();
1431 writePendingOperationsLocked();
1432 writeStatisticsLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001433 }
1434 }
1435
1436 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001437 * Read all account information back in to the initial engine state.
1438 */
1439 private void readAccountInfoLocked() {
Fred Quintana77c560f2010-03-29 22:20:26 -07001440 int highestAuthorityId = -1;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001441 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001443 fis = mAccountInfoFile.openRead();
1444 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1445 XmlPullParser parser = Xml.newPullParser();
1446 parser.setInput(fis, null);
1447 int eventType = parser.getEventType();
1448 while (eventType != XmlPullParser.START_TAG) {
1449 eventType = parser.next();
1450 }
1451 String tagName = parser.getName();
1452 if ("accounts".equals(tagName)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001453 String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
Fred Quintanac2e46912010-03-15 16:10:44 -07001454 String versionString = parser.getAttributeValue(null, "version");
1455 int version;
1456 try {
1457 version = (versionString == null) ? 0 : Integer.parseInt(versionString);
1458 } catch (NumberFormatException e) {
1459 version = 0;
1460 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001461 String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
Fred Quintana77c560f2010-03-29 22:20:26 -07001462 try {
1463 int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
1464 mNextAuthorityId = Math.max(mNextAuthorityId, id);
1465 } catch (NumberFormatException e) {
1466 // don't care
Fred Quintanac2e46912010-03-15 16:10:44 -07001467 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001468 mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001469 eventType = parser.next();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001470 AuthorityInfo authority = null;
1471 Pair<Bundle, Long> periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001472 do {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001473 if (eventType == XmlPullParser.START_TAG) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001474 tagName = parser.getName();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001475 if (parser.getDepth() == 2) {
1476 if ("authority".equals(tagName)) {
Fred Quintanac2e46912010-03-15 16:10:44 -07001477 authority = parseAuthority(parser, version);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001478 periodicSync = null;
Fred Quintana77c560f2010-03-29 22:20:26 -07001479 if (authority.ident > highestAuthorityId) {
1480 highestAuthorityId = authority.ident;
1481 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001482 } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) {
1483 parseListenForTickles(parser);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001484 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001485 } else if (parser.getDepth() == 3) {
1486 if ("periodicSync".equals(tagName) && authority != null) {
1487 periodicSync = parsePeriodicSync(parser, authority);
1488 }
1489 } else if (parser.getDepth() == 4 && periodicSync != null) {
1490 if ("extra".equals(tagName)) {
1491 parseExtra(parser, periodicSync);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001492 }
1493 }
1494 }
1495 eventType = parser.next();
1496 } while (eventType != XmlPullParser.END_DOCUMENT);
1497 }
1498 } catch (XmlPullParserException e) {
1499 Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001500 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001501 } catch (java.io.IOException e) {
1502 if (fis == null) Log.i(TAG, "No initial accounts");
1503 else Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001504 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001505 } finally {
Fred Quintana77c560f2010-03-29 22:20:26 -07001506 mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001507 if (fis != null) {
1508 try {
1509 fis.close();
1510 } catch (java.io.IOException e1) {
1511 }
1512 }
1513 }
Fred Quintanac2e46912010-03-15 16:10:44 -07001514
Fred Quintana77c560f2010-03-29 22:20:26 -07001515 maybeMigrateSettingsForRenamedAuthorities();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001516 }
Costin Manolache360e4542009-09-04 13:36:04 -07001517
Fred Quintanafb084402010-03-23 17:57:03 -07001518 /**
1519 * some authority names have changed. copy over their settings and delete the old ones
1520 * @return true if a change was made
1521 */
1522 private boolean maybeMigrateSettingsForRenamedAuthorities() {
1523 boolean writeNeeded = false;
1524
1525 ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
1526 final int N = mAuthorities.size();
1527 for (int i=0; i<N; i++) {
1528 AuthorityInfo authority = mAuthorities.valueAt(i);
1529 // skip this authority if it isn't one of the renamed ones
1530 final String newAuthorityName = sAuthorityRenames.get(authority.authority);
1531 if (newAuthorityName == null) {
1532 continue;
1533 }
1534
1535 // remember this authority so we can remove it later. we can't remove it
1536 // now without messing up this loop iteration
1537 authoritiesToRemove.add(authority);
1538
1539 // this authority isn't enabled, no need to copy it to the new authority name since
1540 // the default is "disabled"
1541 if (!authority.enabled) {
1542 continue;
1543 }
1544
1545 // if we already have a record of this new authority then don't copy over the settings
Amith Yamasani04e0d262012-02-14 11:50:53 -08001546 if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup")
1547 != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001548 continue;
1549 }
1550
1551 AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001552 authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001553 newAuthority.enabled = true;
1554 writeNeeded = true;
1555 }
1556
1557 for (AuthorityInfo authorityInfo : authoritiesToRemove) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001558 removeAuthorityLocked(authorityInfo.account, authorityInfo.userId,
1559 authorityInfo.authority, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001560 writeNeeded = true;
1561 }
1562
1563 return writeNeeded;
1564 }
1565
Amith Yamasani04e0d262012-02-14 11:50:53 -08001566 private void parseListenForTickles(XmlPullParser parser) {
1567 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1568 int userId = 0;
1569 try {
1570 userId = Integer.parseInt(user);
1571 } catch (NumberFormatException e) {
1572 Log.e(TAG, "error parsing the user for listen-for-tickles", e);
1573 } catch (NullPointerException e) {
1574 Log.e(TAG, "the user in listen-for-tickles is null", e);
1575 }
1576 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
1577 boolean listen = enabled == null || Boolean.parseBoolean(enabled);
1578 mMasterSyncAutomatically.put(userId, listen);
1579 }
1580
Fred Quintanac2e46912010-03-15 16:10:44 -07001581 private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001582 AuthorityInfo authority = null;
1583 int id = -1;
1584 try {
1585 id = Integer.parseInt(parser.getAttributeValue(
1586 null, "id"));
1587 } catch (NumberFormatException e) {
1588 Log.e(TAG, "error parsing the id of the authority", e);
1589 } catch (NullPointerException e) {
1590 Log.e(TAG, "the id of the authority is null", e);
1591 }
1592 if (id >= 0) {
Fred Quintanafb084402010-03-23 17:57:03 -07001593 String authorityName = parser.getAttributeValue(null, "authority");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001594 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
Fred Quintanafb084402010-03-23 17:57:03 -07001595 String syncable = parser.getAttributeValue(null, "syncable");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001596 String accountName = parser.getAttributeValue(null, "account");
1597 String accountType = parser.getAttributeValue(null, "type");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001598 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1599 int userId = user == null ? 0 : Integer.parseInt(user);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001600 if (accountType == null) {
1601 accountType = "com.google";
Fred Quintanafb084402010-03-23 17:57:03 -07001602 syncable = "unknown";
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001603 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001604 authority = mAuthorities.get(id);
1605 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1606 + accountName + " auth=" + authorityName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001607 + " user=" + userId
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001608 + " enabled=" + enabled
1609 + " syncable=" + syncable);
1610 if (authority == null) {
1611 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1612 authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001613 new Account(accountName, accountType), userId, authorityName, id, false);
Fred Quintanac2e46912010-03-15 16:10:44 -07001614 // If the version is 0 then we are upgrading from a file format that did not
1615 // know about periodic syncs. In that case don't clear the list since we
1616 // want the default, which is a daily periodioc sync.
1617 // Otherwise clear out this default list since we will populate it later with
1618 // the periodic sync descriptions that are read from the configuration file.
1619 if (version > 0) {
1620 authority.periodicSyncs.clear();
1621 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001622 }
1623 if (authority != null) {
1624 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
1625 if ("unknown".equals(syncable)) {
1626 authority.syncable = -1;
1627 } else {
1628 authority.syncable =
Fred Quintanafb084402010-03-23 17:57:03 -07001629 (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001630 }
1631 } else {
1632 Log.w(TAG, "Failure adding authority: account="
1633 + accountName + " auth=" + authorityName
1634 + " enabled=" + enabled
1635 + " syncable=" + syncable);
1636 }
1637 }
1638
1639 return authority;
1640 }
1641
1642 private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
1643 Bundle extras = new Bundle();
1644 String periodValue = parser.getAttributeValue(null, "period");
1645 final long period;
1646 try {
1647 period = Long.parseLong(periodValue);
1648 } catch (NumberFormatException e) {
1649 Log.e(TAG, "error parsing the period of a periodic sync", e);
1650 return null;
1651 } catch (NullPointerException e) {
1652 Log.e(TAG, "the period of a periodic sync is null", e);
1653 return null;
1654 }
1655 final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
1656 authority.periodicSyncs.add(periodicSync);
Fred Quintanac2e46912010-03-15 16:10:44 -07001657
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001658 return periodicSync;
1659 }
1660
1661 private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
1662 final Bundle extras = periodicSync.first;
1663 String name = parser.getAttributeValue(null, "name");
1664 String type = parser.getAttributeValue(null, "type");
1665 String value1 = parser.getAttributeValue(null, "value1");
1666 String value2 = parser.getAttributeValue(null, "value2");
1667
1668 try {
1669 if ("long".equals(type)) {
1670 extras.putLong(name, Long.parseLong(value1));
1671 } else if ("integer".equals(type)) {
1672 extras.putInt(name, Integer.parseInt(value1));
1673 } else if ("double".equals(type)) {
1674 extras.putDouble(name, Double.parseDouble(value1));
1675 } else if ("float".equals(type)) {
1676 extras.putFloat(name, Float.parseFloat(value1));
1677 } else if ("boolean".equals(type)) {
1678 extras.putBoolean(name, Boolean.parseBoolean(value1));
1679 } else if ("string".equals(type)) {
1680 extras.putString(name, value1);
1681 } else if ("account".equals(type)) {
1682 extras.putParcelable(name, new Account(value1, value2));
1683 }
1684 } catch (NumberFormatException e) {
1685 Log.e(TAG, "error parsing bundle value", e);
1686 } catch (NullPointerException e) {
1687 Log.e(TAG, "error parsing bundle value", e);
1688 }
1689 }
1690
Dianne Hackborn231cc602009-04-27 17:10:36 -07001691 /**
1692 * Write all account information to the account file.
1693 */
1694 private void writeAccountInfoLocked() {
1695 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1696 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001697
Dianne Hackborn231cc602009-04-27 17:10:36 -07001698 try {
1699 fos = mAccountInfoFile.startWrite();
1700 XmlSerializer out = new FastXmlSerializer();
1701 out.setOutput(fos, "utf-8");
1702 out.startDocument(null, true);
1703 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001704
Dianne Hackborn231cc602009-04-27 17:10:36 -07001705 out.startTag(null, "accounts");
Fred Quintanac2e46912010-03-15 16:10:44 -07001706 out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001707 out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
1708
1709 // Write the Sync Automatically flags for each user
1710 final int M = mMasterSyncAutomatically.size();
1711 for (int m = 0; m < M; m++) {
1712 int userId = mMasterSyncAutomatically.keyAt(m);
1713 Boolean listen = mMasterSyncAutomatically.valueAt(m);
1714 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
1715 out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
1716 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
1717 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001718 }
Costin Manolache360e4542009-09-04 13:36:04 -07001719
Dianne Hackborn231cc602009-04-27 17:10:36 -07001720 final int N = mAuthorities.size();
1721 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001722 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001723 out.startTag(null, "authority");
1724 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001725 out.attribute(null, "account", authority.account.name);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001726 out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001727 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001728 out.attribute(null, "authority", authority.authority);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001729 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
Fred Quintana5e787c42009-08-16 23:13:53 -07001730 if (authority.syncable < 0) {
1731 out.attribute(null, "syncable", "unknown");
Fred Quintanafb084402010-03-23 17:57:03 -07001732 } else {
1733 out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
Fred Quintana5e787c42009-08-16 23:13:53 -07001734 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001735 for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
1736 out.startTag(null, "periodicSync");
1737 out.attribute(null, "period", Long.toString(periodicSync.second));
1738 final Bundle extras = periodicSync.first;
1739 for (String key : extras.keySet()) {
1740 out.startTag(null, "extra");
1741 out.attribute(null, "name", key);
1742 final Object value = extras.get(key);
1743 if (value instanceof Long) {
1744 out.attribute(null, "type", "long");
1745 out.attribute(null, "value1", value.toString());
1746 } else if (value instanceof Integer) {
1747 out.attribute(null, "type", "integer");
1748 out.attribute(null, "value1", value.toString());
1749 } else if (value instanceof Boolean) {
1750 out.attribute(null, "type", "boolean");
1751 out.attribute(null, "value1", value.toString());
1752 } else if (value instanceof Float) {
1753 out.attribute(null, "type", "float");
1754 out.attribute(null, "value1", value.toString());
1755 } else if (value instanceof Double) {
1756 out.attribute(null, "type", "double");
1757 out.attribute(null, "value1", value.toString());
1758 } else if (value instanceof String) {
1759 out.attribute(null, "type", "string");
1760 out.attribute(null, "value1", value.toString());
1761 } else if (value instanceof Account) {
1762 out.attribute(null, "type", "account");
1763 out.attribute(null, "value1", ((Account)value).name);
1764 out.attribute(null, "value2", ((Account)value).type);
1765 }
1766 out.endTag(null, "extra");
1767 }
1768 out.endTag(null, "periodicSync");
1769 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001770 out.endTag(null, "authority");
1771 }
Costin Manolache360e4542009-09-04 13:36:04 -07001772
Dianne Hackborn231cc602009-04-27 17:10:36 -07001773 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001774
Dianne Hackborn231cc602009-04-27 17:10:36 -07001775 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001776
Dianne Hackborn231cc602009-04-27 17:10:36 -07001777 mAccountInfoFile.finishWrite(fos);
1778 } catch (java.io.IOException e1) {
1779 Log.w(TAG, "Error writing accounts", e1);
1780 if (fos != null) {
1781 mAccountInfoFile.failWrite(fos);
1782 }
1783 }
1784 }
Costin Manolache360e4542009-09-04 13:36:04 -07001785
Dianne Hackborn231cc602009-04-27 17:10:36 -07001786 static int getIntColumn(Cursor c, String name) {
1787 return c.getInt(c.getColumnIndex(name));
1788 }
Costin Manolache360e4542009-09-04 13:36:04 -07001789
Dianne Hackborn231cc602009-04-27 17:10:36 -07001790 static long getLongColumn(Cursor c, String name) {
1791 return c.getLong(c.getColumnIndex(name));
1792 }
Costin Manolache360e4542009-09-04 13:36:04 -07001793
Dianne Hackborn231cc602009-04-27 17:10:36 -07001794 /**
1795 * Load sync engine state from the old syncmanager database, and then
1796 * erase it. Note that we don't deal with pending operations, active
1797 * sync, or history.
1798 */
Fred Quintana77c560f2010-03-29 22:20:26 -07001799 private void readAndDeleteLegacyAccountInfoLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001800 // Look for old database to initialize from.
1801 File file = mContext.getDatabasePath("syncmanager.db");
1802 if (!file.exists()) {
1803 return;
1804 }
1805 String path = file.getPath();
1806 SQLiteDatabase db = null;
1807 try {
1808 db = SQLiteDatabase.openDatabase(path, null,
1809 SQLiteDatabase.OPEN_READONLY);
1810 } catch (SQLiteException e) {
1811 }
Costin Manolache360e4542009-09-04 13:36:04 -07001812
Dianne Hackborn231cc602009-04-27 17:10:36 -07001813 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001814 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001815
Dianne Hackborn231cc602009-04-27 17:10:36 -07001816 // Copy in all of the status information, as well as accounts.
1817 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1818 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1819 qb.setTables("stats, status");
1820 HashMap<String,String> map = new HashMap<String,String>();
1821 map.put("_id", "status._id as _id");
1822 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001823 if (hasType) {
1824 map.put("account_type", "stats.account_type as account_type");
1825 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001826 map.put("authority", "stats.authority as authority");
1827 map.put("totalElapsedTime", "totalElapsedTime");
1828 map.put("numSyncs", "numSyncs");
1829 map.put("numSourceLocal", "numSourceLocal");
1830 map.put("numSourcePoll", "numSourcePoll");
1831 map.put("numSourceServer", "numSourceServer");
1832 map.put("numSourceUser", "numSourceUser");
1833 map.put("lastSuccessSource", "lastSuccessSource");
1834 map.put("lastSuccessTime", "lastSuccessTime");
1835 map.put("lastFailureSource", "lastFailureSource");
1836 map.put("lastFailureTime", "lastFailureTime");
1837 map.put("lastFailureMesg", "lastFailureMesg");
1838 map.put("pending", "pending");
1839 qb.setProjectionMap(map);
1840 qb.appendWhere("stats._id = status.stats_id");
1841 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001843 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001844 String accountType = hasType
1845 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001846 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001847 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001849 String authorityName = c.getString(c.getColumnIndex("authority"));
1850 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001851 new Account(accountName, accountType), 0 /* legacy is single-user */,
Dianne Hackborn7a135592009-05-06 00:28:37 -07001852 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001853 if (authority != null) {
1854 int i = mSyncStatus.size();
1855 boolean found = false;
1856 SyncStatusInfo st = null;
1857 while (i > 0) {
1858 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001859 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001860 if (st.authorityId == authority.ident) {
1861 found = true;
1862 break;
1863 }
1864 }
1865 if (!found) {
1866 st = new SyncStatusInfo(authority.ident);
1867 mSyncStatus.put(authority.ident, st);
1868 }
1869 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1870 st.numSyncs = getIntColumn(c, "numSyncs");
1871 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1872 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1873 st.numSourceServer = getIntColumn(c, "numSourceServer");
1874 st.numSourceUser = getIntColumn(c, "numSourceUser");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001875 st.numSourcePeriodic = 0;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001876 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1877 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1878 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1879 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1880 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1881 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 }
Costin Manolache360e4542009-09-04 13:36:04 -07001884
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001886
Dianne Hackborn231cc602009-04-27 17:10:36 -07001887 // Retrieve the settings.
1888 qb = new SQLiteQueryBuilder();
1889 qb.setTables("settings");
1890 c = qb.query(db, null, null, null, null, null, null);
1891 while (c.moveToNext()) {
1892 String name = c.getString(c.getColumnIndex("name"));
1893 String value = c.getString(c.getColumnIndex("value"));
1894 if (name == null) continue;
1895 if (name.equals("listen_for_tickles")) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001896 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001897 } else if (name.startsWith("sync_provider_")) {
1898 String provider = name.substring("sync_provider_".length(),
1899 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001900 int i = mAuthorities.size();
1901 while (i > 0) {
1902 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001903 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001904 if (authority.authority.equals(provider)) {
1905 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001906 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001907 }
1908 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001909 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910 }
Costin Manolache360e4542009-09-04 13:36:04 -07001911
Dianne Hackborn231cc602009-04-27 17:10:36 -07001912 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001913
Dianne Hackborn231cc602009-04-27 17:10:36 -07001914 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001915
Dianne Hackborn231cc602009-04-27 17:10:36 -07001916 (new File(path)).delete();
1917 }
1918 }
Costin Manolache360e4542009-09-04 13:36:04 -07001919
Dianne Hackborn231cc602009-04-27 17:10:36 -07001920 public static final int STATUS_FILE_END = 0;
1921 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001922
Dianne Hackborn231cc602009-04-27 17:10:36 -07001923 /**
1924 * Read all sync status back in to the initial engine state.
1925 */
1926 private void readStatusLocked() {
1927 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1928 try {
1929 byte[] data = mStatusFile.readFully();
1930 Parcel in = Parcel.obtain();
1931 in.unmarshall(data, 0, data.length);
1932 in.setDataPosition(0);
1933 int token;
1934 while ((token=in.readInt()) != STATUS_FILE_END) {
1935 if (token == STATUS_FILE_ITEM) {
1936 SyncStatusInfo status = new SyncStatusInfo(in);
1937 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1938 status.pending = false;
1939 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1940 + status.authorityId);
1941 mSyncStatus.put(status.authorityId, status);
1942 }
1943 } else {
1944 // Ooops.
1945 Log.w(TAG, "Unknown status token: " + token);
1946 break;
1947 }
1948 }
1949 } catch (java.io.IOException e) {
1950 Log.i(TAG, "No initial status");
1951 }
1952 }
Costin Manolache360e4542009-09-04 13:36:04 -07001953
Dianne Hackborn231cc602009-04-27 17:10:36 -07001954 /**
1955 * Write all sync status to the sync status file.
1956 */
1957 private void writeStatusLocked() {
1958 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001959
Dianne Hackborn231cc602009-04-27 17:10:36 -07001960 // The file is being written, so we don't need to have a scheduled
1961 // write until the next change.
1962 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001963
Dianne Hackborn231cc602009-04-27 17:10:36 -07001964 FileOutputStream fos = null;
1965 try {
1966 fos = mStatusFile.startWrite();
1967 Parcel out = Parcel.obtain();
1968 final int N = mSyncStatus.size();
1969 for (int i=0; i<N; i++) {
1970 SyncStatusInfo status = mSyncStatus.valueAt(i);
1971 out.writeInt(STATUS_FILE_ITEM);
1972 status.writeToParcel(out, 0);
1973 }
1974 out.writeInt(STATUS_FILE_END);
1975 fos.write(out.marshall());
1976 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001977
Dianne Hackborn231cc602009-04-27 17:10:36 -07001978 mStatusFile.finishWrite(fos);
1979 } catch (java.io.IOException e1) {
1980 Log.w(TAG, "Error writing status", e1);
1981 if (fos != null) {
1982 mStatusFile.failWrite(fos);
1983 }
1984 }
1985 }
Costin Manolache360e4542009-09-04 13:36:04 -07001986
Fred Quintana307da1a2010-01-21 14:24:20 -08001987 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07001988
Dianne Hackborn231cc602009-04-27 17:10:36 -07001989 /**
1990 * Read all pending operations back in to the initial engine state.
1991 */
1992 private void readPendingOperationsLocked() {
1993 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1994 try {
1995 byte[] data = mPendingFile.readFully();
1996 Parcel in = Parcel.obtain();
1997 in.unmarshall(data, 0, data.length);
1998 in.setDataPosition(0);
1999 final int SIZE = in.dataSize();
2000 while (in.dataPosition() < SIZE) {
2001 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08002002 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002003 Log.w(TAG, "Unknown pending operation version "
2004 + version + "; dropping all ops");
2005 break;
2006 }
2007 int authorityId = in.readInt();
2008 int syncSource = in.readInt();
2009 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08002010 boolean expedited;
2011 if (version == PENDING_OPERATION_VERSION) {
2012 expedited = in.readInt() != 0;
2013 } else {
2014 expedited = false;
2015 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002016 AuthorityInfo authority = mAuthorities.get(authorityId);
2017 if (authority != null) {
Fred Quintana5695c7b2010-12-06 15:07:52 -08002018 Bundle extras;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002019 if (flatExtras != null) {
2020 extras = unflattenBundle(flatExtras);
Fred Quintana5695c7b2010-12-06 15:07:52 -08002021 } else {
2022 // if we are unable to parse the extras for whatever reason convert this
2023 // to a regular sync by creating an empty extras
2024 extras = new Bundle();
Dianne Hackborn231cc602009-04-27 17:10:36 -07002025 }
2026 PendingOperation op = new PendingOperation(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002027 authority.account, authority.userId, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08002028 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002029 op.authorityId = authorityId;
2030 op.flatExtras = flatExtras;
2031 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
2032 + " auth=" + op.authority
2033 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08002034 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07002035 + " extras=" + op.extras);
2036 mPendingOperations.add(op);
2037 }
2038 }
2039 } catch (java.io.IOException e) {
2040 Log.i(TAG, "No initial pending operations");
2041 }
2042 }
Costin Manolache360e4542009-09-04 13:36:04 -07002043
Dianne Hackborn231cc602009-04-27 17:10:36 -07002044 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
2045 out.writeInt(PENDING_OPERATION_VERSION);
2046 out.writeInt(op.authorityId);
2047 out.writeInt(op.syncSource);
2048 if (op.flatExtras == null && op.extras != null) {
2049 op.flatExtras = flattenBundle(op.extras);
2050 }
2051 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08002052 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002053 }
Costin Manolache360e4542009-09-04 13:36:04 -07002054
Dianne Hackborn231cc602009-04-27 17:10:36 -07002055 /**
2056 * Write all currently pending ops to the pending ops file.
2057 */
2058 private void writePendingOperationsLocked() {
2059 final int N = mPendingOperations.size();
2060 FileOutputStream fos = null;
2061 try {
2062 if (N == 0) {
2063 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
2064 mPendingFile.truncate();
2065 return;
2066 }
Costin Manolache360e4542009-09-04 13:36:04 -07002067
Dianne Hackborn231cc602009-04-27 17:10:36 -07002068 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
2069 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07002070
Dianne Hackborn231cc602009-04-27 17:10:36 -07002071 Parcel out = Parcel.obtain();
2072 for (int i=0; i<N; i++) {
2073 PendingOperation op = mPendingOperations.get(i);
2074 writePendingOperationLocked(op, out);
2075 }
2076 fos.write(out.marshall());
2077 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002078
Dianne Hackborn231cc602009-04-27 17:10:36 -07002079 mPendingFile.finishWrite(fos);
2080 } catch (java.io.IOException e1) {
2081 Log.w(TAG, "Error writing pending operations", e1);
2082 if (fos != null) {
2083 mPendingFile.failWrite(fos);
2084 }
2085 }
2086 }
Costin Manolache360e4542009-09-04 13:36:04 -07002087
Dianne Hackborn231cc602009-04-27 17:10:36 -07002088 /**
2089 * Append the given operation to the pending ops file; if unable to,
2090 * write all pending ops.
2091 */
2092 private void appendPendingOperationLocked(PendingOperation op) {
2093 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
2094 FileOutputStream fos = null;
2095 try {
2096 fos = mPendingFile.openAppend();
2097 } catch (java.io.IOException e) {
2098 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
2099 writePendingOperationsLocked();
2100 return;
2101 }
Costin Manolache360e4542009-09-04 13:36:04 -07002102
Dianne Hackborn231cc602009-04-27 17:10:36 -07002103 try {
2104 Parcel out = Parcel.obtain();
2105 writePendingOperationLocked(op, out);
2106 fos.write(out.marshall());
2107 out.recycle();
2108 } catch (java.io.IOException e1) {
2109 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002111 try {
2112 fos.close();
2113 } catch (java.io.IOException e2) {
2114 }
2115 }
2116 }
Costin Manolache360e4542009-09-04 13:36:04 -07002117
Dianne Hackborn231cc602009-04-27 17:10:36 -07002118 static private byte[] flattenBundle(Bundle bundle) {
2119 byte[] flatData = null;
2120 Parcel parcel = Parcel.obtain();
2121 try {
2122 bundle.writeToParcel(parcel, 0);
2123 flatData = parcel.marshall();
2124 } finally {
2125 parcel.recycle();
2126 }
2127 return flatData;
2128 }
Costin Manolache360e4542009-09-04 13:36:04 -07002129
Dianne Hackborn231cc602009-04-27 17:10:36 -07002130 static private Bundle unflattenBundle(byte[] flatData) {
2131 Bundle bundle;
2132 Parcel parcel = Parcel.obtain();
2133 try {
2134 parcel.unmarshall(flatData, 0, flatData.length);
2135 parcel.setDataPosition(0);
2136 bundle = parcel.readBundle();
2137 } catch (RuntimeException e) {
2138 // A RuntimeException is thrown if we were unable to parse the parcel.
2139 // Create an empty parcel in this case.
2140 bundle = new Bundle();
2141 } finally {
2142 parcel.recycle();
2143 }
2144 return bundle;
2145 }
Costin Manolache360e4542009-09-04 13:36:04 -07002146
Amith Yamasani04e0d262012-02-14 11:50:53 -08002147 private void requestSync(Account account, int userId, String authority, Bundle extras) {
2148 // If this is happening in the system process, then call the syncrequest listener
2149 // to make a request back to the SyncManager directly.
2150 // If this is probably a test instance, then call back through the ContentResolver
2151 // which will know which userId to apply based on the Binder id.
2152 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
2153 && mSyncRequestListener != null) {
2154 mSyncRequestListener.onSyncRequest(account, userId, authority, extras);
2155 } else {
2156 ContentResolver.requestSync(account, authority, extras);
2157 }
2158 }
2159
Dianne Hackborn231cc602009-04-27 17:10:36 -07002160 public static final int STATISTICS_FILE_END = 0;
2161 public static final int STATISTICS_FILE_ITEM_OLD = 100;
2162 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07002163
Dianne Hackborn231cc602009-04-27 17:10:36 -07002164 /**
2165 * Read all sync statistics back in to the initial engine state.
2166 */
2167 private void readStatisticsLocked() {
2168 try {
2169 byte[] data = mStatisticsFile.readFully();
2170 Parcel in = Parcel.obtain();
2171 in.unmarshall(data, 0, data.length);
2172 in.setDataPosition(0);
2173 int token;
2174 int index = 0;
2175 while ((token=in.readInt()) != STATISTICS_FILE_END) {
2176 if (token == STATISTICS_FILE_ITEM
2177 || token == STATISTICS_FILE_ITEM_OLD) {
2178 int day = in.readInt();
2179 if (token == STATISTICS_FILE_ITEM_OLD) {
2180 day = day - 2009 + 14245; // Magic!
2181 }
2182 DayStats ds = new DayStats(day);
2183 ds.successCount = in.readInt();
2184 ds.successTime = in.readLong();
2185 ds.failureCount = in.readInt();
2186 ds.failureTime = in.readLong();
2187 if (index < mDayStats.length) {
2188 mDayStats[index] = ds;
2189 index++;
2190 }
2191 } else {
2192 // Ooops.
2193 Log.w(TAG, "Unknown stats token: " + token);
2194 break;
2195 }
2196 }
2197 } catch (java.io.IOException e) {
2198 Log.i(TAG, "No initial statistics");
2199 }
2200 }
Costin Manolache360e4542009-09-04 13:36:04 -07002201
Dianne Hackborn231cc602009-04-27 17:10:36 -07002202 /**
2203 * Write all sync statistics to the sync status file.
2204 */
2205 private void writeStatisticsLocked() {
2206 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07002207
Dianne Hackborn231cc602009-04-27 17:10:36 -07002208 // The file is being written, so we don't need to have a scheduled
2209 // write until the next change.
2210 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07002211
Dianne Hackborn231cc602009-04-27 17:10:36 -07002212 FileOutputStream fos = null;
2213 try {
2214 fos = mStatisticsFile.startWrite();
2215 Parcel out = Parcel.obtain();
2216 final int N = mDayStats.length;
2217 for (int i=0; i<N; i++) {
2218 DayStats ds = mDayStats[i];
2219 if (ds == null) {
2220 break;
2221 }
2222 out.writeInt(STATISTICS_FILE_ITEM);
2223 out.writeInt(ds.day);
2224 out.writeInt(ds.successCount);
2225 out.writeLong(ds.successTime);
2226 out.writeInt(ds.failureCount);
2227 out.writeLong(ds.failureTime);
2228 }
2229 out.writeInt(STATISTICS_FILE_END);
2230 fos.write(out.marshall());
2231 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002232
Dianne Hackborn231cc602009-04-27 17:10:36 -07002233 mStatisticsFile.finishWrite(fos);
2234 } catch (java.io.IOException e1) {
2235 Log.w(TAG, "Error writing stats", e1);
2236 if (fos != null) {
2237 mStatisticsFile.failWrite(fos);
2238 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002239 }
2240 }
2241}