blob: 984c070b8265bab19bccdb773eaeb38b4887334c [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
Dianne Hackborn231cc602009-04-27 17:10:36 -070023import 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.database.sqlite.SQLiteDatabase;
Dianne Hackborn231cc602009-04-27 17:10:36 -070030import android.database.sqlite.SQLiteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.database.sqlite.SQLiteQueryBuilder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070032import android.os.Bundle;
33import android.os.Environment;
34import android.os.Handler;
35import android.os.Message;
36import android.os.Parcel;
37import android.os.RemoteCallbackList;
38import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.util.Log;
Dianne Hackborn231cc602009-04-27 17:10:36 -070040import android.util.SparseArray;
41import android.util.Xml;
Fred Quintana307da1a2010-01-21 14:24:20 -080042import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
Dianne Hackborn231cc602009-04-27 17:10:36 -070044import java.io.File;
45import java.io.FileInputStream;
46import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.util.ArrayList;
Dianne Hackborn231cc602009-04-27 17:10:36 -070048import java.util.Calendar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070050import java.util.Iterator;
51import java.util.TimeZone;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080052import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54/**
Dianne Hackborn231cc602009-04-27 17:10:36 -070055 * Singleton that tracks the sync data and overall sync
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 * history on the device.
Costin Manolache360e4542009-09-04 13:36:04 -070057 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 * @hide
59 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070060public class SyncStorageEngine extends Handler {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 private static final String TAG = "SyncManager";
Dianne Hackborn231cc602009-04-27 17:10:36 -070062 private static final boolean DEBUG_FILE = false;
Costin Manolache360e4542009-09-04 13:36:04 -070063
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080064 private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
65
Dianne Hackborn231cc602009-04-27 17:10:36 -070066 // @VisibleForTesting
67 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
Dianne Hackborn231cc602009-04-27 17:10:36 -070069 /** Enum value for a sync start event. */
70 public static final int EVENT_START = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
Dianne Hackborn231cc602009-04-27 17:10:36 -070072 /** Enum value for a sync stop event. */
73 public static final int EVENT_STOP = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
Dianne Hackborn231cc602009-04-27 17:10:36 -070075 // TODO: i18n -- grab these out of resources.
76 /** String names for the sync event types. */
77 public static final String[] EVENTS = { "START", "STOP" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Dianne Hackborn231cc602009-04-27 17:10:36 -070079 /** Enum value for a server-initiated sync. */
80 public static final int SOURCE_SERVER = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
Dianne Hackborn231cc602009-04-27 17:10:36 -070082 /** Enum value for a local-initiated sync. */
83 public static final int SOURCE_LOCAL = 1;
84 /**
85 * Enum value for a poll-based sync (e.g., upon connection to
86 * network)
87 */
88 public static final int SOURCE_POLL = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
Dianne Hackborn231cc602009-04-27 17:10:36 -070090 /** Enum value for a user-initiated sync. */
91 public static final int SOURCE_USER = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080093 /** Enum value for a periodic sync. */
94 public static final int SOURCE_PERIODIC = 4;
95
Fred Quintana307da1a2010-01-21 14:24:20 -080096 public static final long NOT_IN_BACKOFF_MODE = -1;
97
Fred Quintanaac9385e2009-06-22 18:00:59 -070098 private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
99 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
100
Dianne Hackborn231cc602009-04-27 17:10:36 -0700101 // TODO: i18n -- grab these out of resources.
102 /** String names for the sync source types. */
103 public static final String[] SOURCES = { "SERVER",
104 "LOCAL",
105 "POLL",
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800106 "USER",
107 "PERIODIC" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
Dianne Hackborn231cc602009-04-27 17:10:36 -0700109 // The MESG column will contain one of these or one of the Error types.
110 public static final String MESG_SUCCESS = "success";
111 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700113 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700114
Dianne Hackborn231cc602009-04-27 17:10:36 -0700115 private static final int MSG_WRITE_STATUS = 1;
116 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700117
Dianne Hackborn231cc602009-04-27 17:10:36 -0700118 private static final int MSG_WRITE_STATISTICS = 2;
119 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700120
121 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700122
Fred Quintanac2e46912010-03-15 16:10:44 -0700123 // the version of the accounts xml file format
Fred Quintanafb084402010-03-23 17:57:03 -0700124 private static final int ACCOUNTS_VERSION = 2;
125
126 private static HashMap<String, String> sAuthorityRenames;
127
128 static {
129 sAuthorityRenames = new HashMap<String, String>();
130 sAuthorityRenames.put("contacts", "com.android.contacts");
131 sAuthorityRenames.put("calendar", "com.android.calendar");
132 }
Fred Quintanac2e46912010-03-15 16:10:44 -0700133
Dianne Hackborn231cc602009-04-27 17:10:36 -0700134 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700135 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700136 final int syncSource;
137 final String authority;
138 final Bundle extras; // note: read-only.
Fred Quintana307da1a2010-01-21 14:24:20 -0800139 final boolean expedited;
Costin Manolache360e4542009-09-04 13:36:04 -0700140
Dianne Hackborn231cc602009-04-27 17:10:36 -0700141 int authorityId;
142 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700143
Dianne Hackborn7a135592009-05-06 00:28:37 -0700144 PendingOperation(Account account, int source,
Fred Quintana307da1a2010-01-21 14:24:20 -0800145 String authority, Bundle extras, boolean expedited) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700146 this.account = account;
147 this.syncSource = source;
148 this.authority = authority;
149 this.extras = extras != null ? new Bundle(extras) : extras;
Fred Quintana307da1a2010-01-21 14:24:20 -0800150 this.expedited = expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700151 this.authorityId = -1;
152 }
153
154 PendingOperation(PendingOperation other) {
155 this.account = other.account;
156 this.syncSource = other.syncSource;
157 this.authority = other.authority;
158 this.extras = other.extras;
159 this.authorityId = other.authorityId;
Fred Quintana307da1a2010-01-21 14:24:20 -0800160 this.expedited = other.expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 }
Costin Manolache360e4542009-09-04 13:36:04 -0700163
Dianne Hackborn231cc602009-04-27 17:10:36 -0700164 static class AccountInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700165 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700166 final HashMap<String, AuthorityInfo> authorities =
167 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700168
Dianne Hackborn7a135592009-05-06 00:28:37 -0700169 AccountInfo(Account account) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700170 this.account = account;
171 }
172 }
Costin Manolache360e4542009-09-04 13:36:04 -0700173
Dianne Hackborn231cc602009-04-27 17:10:36 -0700174 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700175 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700176 final String authority;
177 final int ident;
178 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700179 int syncable;
Fred Quintana307da1a2010-01-21 14:24:20 -0800180 long backoffTime;
181 long backoffDelay;
182 long delayUntil;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800183 final ArrayList<Pair<Bundle, Long>> periodicSyncs;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700184
Dianne Hackborn7a135592009-05-06 00:28:37 -0700185 AuthorityInfo(Account account, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700186 this.account = account;
187 this.authority = authority;
188 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700189 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700190 syncable = -1; // default to "unknown"
Fred Quintana307da1a2010-01-21 14:24:20 -0800191 backoffTime = -1; // if < 0 then we aren't in backoff mode
192 backoffDelay = -1; // if < 0 then we aren't in backoff mode
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800193 periodicSyncs = new ArrayList<Pair<Bundle, Long>>();
194 periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700195 }
196 }
Costin Manolache360e4542009-09-04 13:36:04 -0700197
Dianne Hackborn231cc602009-04-27 17:10:36 -0700198 public static class SyncHistoryItem {
199 int authorityId;
200 int historyId;
201 long eventTime;
202 long elapsedTime;
203 int source;
204 int event;
205 long upstreamActivity;
206 long downstreamActivity;
207 String mesg;
208 }
Costin Manolache360e4542009-09-04 13:36:04 -0700209
Dianne Hackborn231cc602009-04-27 17:10:36 -0700210 public static class DayStats {
211 public final int day;
212 public int successCount;
213 public long successTime;
214 public int failureCount;
215 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700216
Dianne Hackborn231cc602009-04-27 17:10:36 -0700217 public DayStats(int day) {
218 this.day = day;
219 }
220 }
Costin Manolache360e4542009-09-04 13:36:04 -0700221
Dianne Hackborn231cc602009-04-27 17:10:36 -0700222 // Primary list of all syncable authorities. Also our global lock.
223 private final SparseArray<AuthorityInfo> mAuthorities =
224 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700225
Dianne Hackborn7a135592009-05-06 00:28:37 -0700226 private final HashMap<Account, AccountInfo> mAccounts =
227 new HashMap<Account, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228
Dianne Hackborn231cc602009-04-27 17:10:36 -0700229 private final ArrayList<PendingOperation> mPendingOperations =
230 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700231
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700232 private SyncInfo mCurrentSync;
Costin Manolache360e4542009-09-04 13:36:04 -0700233
Dianne Hackborn231cc602009-04-27 17:10:36 -0700234 private final SparseArray<SyncStatusInfo> mSyncStatus =
235 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700236
Dianne Hackborn231cc602009-04-27 17:10:36 -0700237 private final ArrayList<SyncHistoryItem> mSyncHistory =
238 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700239
Dianne Hackborn231cc602009-04-27 17:10:36 -0700240 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
241 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700242
Fred Quintana77c560f2010-03-29 22:20:26 -0700243 private int mNextAuthorityId = 0;
244
Dianne Hackborn231cc602009-04-27 17:10:36 -0700245 // We keep 4 weeks of stats.
246 private final DayStats[] mDayStats = new DayStats[7*4];
247 private final Calendar mCal;
248 private int mYear;
249 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700250
Dianne Hackborn231cc602009-04-27 17:10:36 -0700251 private final Context mContext;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800252
Dianne Hackborn231cc602009-04-27 17:10:36 -0700253 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700254
Dianne Hackborn231cc602009-04-27 17:10:36 -0700255 /**
256 * This file contains the core engine state: all accounts and the
257 * settings for them. It must never be lost, and should be changed
258 * infrequently, so it is stored as an XML file.
259 */
260 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700261
Dianne Hackborn231cc602009-04-27 17:10:36 -0700262 /**
263 * This file contains the current sync status. We would like to retain
264 * it across boots, but its loss is not the end of the world, so we store
265 * this information as binary data.
266 */
267 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700268
Dianne Hackborn231cc602009-04-27 17:10:36 -0700269 /**
270 * This file contains sync statistics. This is purely debugging information
271 * so is written infrequently and can be thrown away at any time.
272 */
273 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700274
Dianne Hackborn231cc602009-04-27 17:10:36 -0700275 /**
276 * This file contains the pending sync operations. It is a binary file,
277 * which must be updated every time an operation is added or removed,
278 * so we have special handling of it.
279 */
280 private final AtomicFile mPendingFile;
281 private static final int PENDING_FINISH_TO_WRITE = 4;
282 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700283
Dianne Hackborn231cc602009-04-27 17:10:36 -0700284 private int mNextHistoryId = 0;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700285 private boolean mMasterSyncAutomatically = true;
Costin Manolache360e4542009-09-04 13:36:04 -0700286
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800287 private SyncStorageEngine(Context context, File dataDir) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700290
Dianne Hackborn231cc602009-04-27 17:10:36 -0700291 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700292
Dianne Hackborn231cc602009-04-27 17:10:36 -0700293 File systemDir = new File(dataDir, "system");
294 File syncDir = new File(systemDir, "sync");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800295 syncDir.mkdirs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700296 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
297 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
298 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
299 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700300
Dianne Hackborn231cc602009-04-27 17:10:36 -0700301 readAccountInfoLocked();
302 readStatusLocked();
303 readPendingOperationsLocked();
304 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -0700305 readAndDeleteLegacyAccountInfoLocked();
306 writeAccountInfoLocked();
307 writeStatusLocked();
308 writePendingOperationsLocked();
309 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311
312 public static SyncStorageEngine newTestInstance(Context context) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800313 return new SyncStorageEngine(context, context.getFilesDir());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315
316 public static void init(Context context) {
317 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800318 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
Oscar Montemayor1f4df902010-03-26 18:44:14 -0700320 File dataDir = Environment.getDataDirectory();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800321 sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 }
323
324 public static SyncStorageEngine getSingleton() {
325 if (sSyncStorageEngine == null) {
326 throw new IllegalStateException("not initialized");
327 }
328 return sSyncStorageEngine;
329 }
330
Dianne Hackborn231cc602009-04-27 17:10:36 -0700331 @Override public void handleMessage(Message msg) {
332 if (msg.what == MSG_WRITE_STATUS) {
333 synchronized (mAccounts) {
334 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700335 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700336 } else if (msg.what == MSG_WRITE_STATISTICS) {
337 synchronized (mAccounts) {
338 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 }
340 }
341 }
Costin Manolache360e4542009-09-04 13:36:04 -0700342
Dianne Hackborn231cc602009-04-27 17:10:36 -0700343 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
344 synchronized (mAuthorities) {
345 mChangeListeners.register(callback, mask);
346 }
347 }
Costin Manolache360e4542009-09-04 13:36:04 -0700348
Dianne Hackborn231cc602009-04-27 17:10:36 -0700349 public void removeStatusChangeListener(ISyncStatusObserver callback) {
350 synchronized (mAuthorities) {
351 mChangeListeners.unregister(callback);
352 }
353 }
Costin Manolache360e4542009-09-04 13:36:04 -0700354
Dianne Hackborn231cc602009-04-27 17:10:36 -0700355 private void reportChange(int which) {
356 ArrayList<ISyncStatusObserver> reports = null;
357 synchronized (mAuthorities) {
358 int i = mChangeListeners.beginBroadcast();
359 while (i > 0) {
360 i--;
361 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
362 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 continue;
364 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700365 if (reports == null) {
366 reports = new ArrayList<ISyncStatusObserver>(i);
367 }
368 reports.add(mChangeListeners.getBroadcastItem(i));
369 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700370 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700371 }
Costin Manolache360e4542009-09-04 13:36:04 -0700372
Fred Quintana77c560f2010-03-29 22:20:26 -0700373 if (Log.isLoggable(TAG, Log.VERBOSE)) {
374 Log.v(TAG, "reportChange " + which + " to: " + reports);
375 }
Costin Manolache360e4542009-09-04 13:36:04 -0700376
Dianne Hackborn231cc602009-04-27 17:10:36 -0700377 if (reports != null) {
378 int i = reports.size();
379 while (i > 0) {
380 i--;
381 try {
382 reports.get(i).onStatusChanged(which);
383 } catch (RemoteException e) {
384 // The remote callback list will take care of this for us.
385 }
386 }
387 }
388 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700389
Fred Quintanaac9385e2009-06-22 18:00:59 -0700390 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700391 synchronized (mAuthorities) {
392 if (account != null) {
393 AuthorityInfo authority = getAuthorityLocked(account, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700394 "getSyncAutomatically");
395 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700396 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700397
Dianne Hackborn231cc602009-04-27 17:10:36 -0700398 int i = mAuthorities.size();
399 while (i > 0) {
400 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700401 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700402 if (authority.authority.equals(providerName)
403 && authority.enabled) {
404 return true;
405 }
406 }
407 return false;
408 }
409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410
Fred Quintanaac9385e2009-06-22 18:00:59 -0700411 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700412 Log.d(TAG, "setSyncAutomatically: " + account + ", provider " + providerName
413 + " -> " + sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700414 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700415 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700416 if (authority.enabled == sync) {
417 Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
418 return;
419 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700420 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700421 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700423
Fred Quintana77c560f2010-03-29 22:20:26 -0700424 if (sync) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800425 ContentResolver.requestSync(account, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700426 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700427 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429
Fred Quintana5e787c42009-08-16 23:13:53 -0700430 public int getIsSyncable(Account account, String providerName) {
431 synchronized (mAuthorities) {
432 if (account != null) {
433 AuthorityInfo authority = getAuthorityLocked(account, providerName,
434 "getIsSyncable");
435 if (authority == null) {
436 return -1;
437 }
438 return authority.syncable;
439 }
440
441 int i = mAuthorities.size();
442 while (i > 0) {
443 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700444 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700445 if (authority.authority.equals(providerName)) {
446 return authority.syncable;
447 }
448 }
449 return -1;
450 }
451 }
452
453 public void setIsSyncable(Account account, String providerName, int syncable) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700454 if (syncable > 1) {
455 syncable = 1;
456 } else if (syncable < -1) {
457 syncable = -1;
458 }
459 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700460 synchronized (mAuthorities) {
461 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700462 if (authority.syncable == syncable) {
463 Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
464 return;
465 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700466 authority.syncable = syncable;
467 writeAccountInfoLocked();
468 }
469
Fred Quintana77c560f2010-03-29 22:20:26 -0700470 if (syncable > 0) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800471 ContentResolver.requestSync(account, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700472 }
473 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
474 }
475
Fred Quintana307da1a2010-01-21 14:24:20 -0800476 public Pair<Long, Long> getBackoff(Account account, String providerName) {
477 synchronized (mAuthorities) {
478 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getBackoff");
479 if (authority == null || authority.backoffTime < 0) {
480 return null;
481 }
482 return Pair.create(authority.backoffTime, authority.backoffDelay);
483 }
484 }
485
486 public void setBackoff(Account account, String providerName,
487 long nextSyncTime, long nextDelay) {
488 if (Log.isLoggable(TAG, Log.VERBOSE)) {
489 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
490 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
491 }
492 boolean changed = false;
493 synchronized (mAuthorities) {
494 if (account == null || providerName == null) {
495 for (AccountInfo accountInfo : mAccounts.values()) {
496 if (account != null && !account.equals(accountInfo.account)) continue;
497 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
498 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
499 continue;
500 }
501 if (authorityInfo.backoffTime != nextSyncTime
502 || authorityInfo.backoffDelay != nextDelay) {
503 authorityInfo.backoffTime = nextSyncTime;
504 authorityInfo.backoffDelay = nextDelay;
505 changed = true;
506 }
507 }
508 }
509 } else {
510 AuthorityInfo authority =
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800511 getOrCreateAuthorityLocked(account, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800512 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
513 return;
514 }
515 authority.backoffTime = nextSyncTime;
516 authority.backoffDelay = nextDelay;
517 changed = true;
518 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800519 }
520
521 if (changed) {
522 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
523 }
524 }
525
526 public void setDelayUntilTime(Account account, String providerName, long delayUntil) {
527 if (Log.isLoggable(TAG, Log.VERBOSE)) {
528 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
529 + " -> delayUntil " + delayUntil);
530 }
531 synchronized (mAuthorities) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800532 AuthorityInfo authority = getOrCreateAuthorityLocked(
533 account, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800534 if (authority.delayUntil == delayUntil) {
535 return;
536 }
537 authority.delayUntil = delayUntil;
Fred Quintana307da1a2010-01-21 14:24:20 -0800538 }
539
540 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
541 }
542
543 public long getDelayUntilTime(Account account, String providerName) {
544 synchronized (mAuthorities) {
545 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getDelayUntil");
546 if (authority == null) {
547 return 0;
548 }
549 return authority.delayUntil;
550 }
551 }
552
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800553 private void updateOrRemovePeriodicSync(Account account, String providerName, Bundle extras,
554 long period, boolean add) {
555 if (period <= 0) {
556 period = 0;
557 }
558 if (extras == null) {
559 extras = new Bundle();
560 }
561 if (Log.isLoggable(TAG, Log.VERBOSE)) {
562 Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", provider " + providerName
563 + " -> period " + period + ", extras " + extras);
564 }
565 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700566 try {
567 AuthorityInfo authority =
568 getOrCreateAuthorityLocked(account, providerName, -1, false);
569 if (add) {
570 // add this periodic sync if one with the same extras doesn't already
571 // exist in the periodicSyncs array
572 boolean alreadyPresent = false;
573 for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
574 Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
575 final Bundle existingExtras = syncInfo.first;
576 if (equals(existingExtras, extras)) {
577 if (syncInfo.second == period) {
578 return;
579 }
580 authority.periodicSyncs.set(i, Pair.create(extras, period));
581 alreadyPresent = true;
582 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800583 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700584 }
585 // if we added an entry to the periodicSyncs array also add an entry to
586 // the periodic syncs status to correspond to it
587 if (!alreadyPresent) {
588 authority.periodicSyncs.add(Pair.create(extras, period));
589 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
590 status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
591 }
592 } else {
593 // remove any periodic syncs that match the authority and extras
594 SyncStatusInfo status = mSyncStatus.get(authority.ident);
595 boolean changed = false;
596 Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
597 int i = 0;
598 while (iterator.hasNext()) {
599 Pair<Bundle, Long> syncInfo = iterator.next();
600 if (equals(syncInfo.first, extras)) {
601 iterator.remove();
602 changed = true;
603 // if we removed an entry from the periodicSyncs array also
604 // remove the corresponding entry from the status
605 if (status != null) {
606 status.removePeriodicSyncTime(i);
607 }
608 } else {
609 i++;
610 }
611 }
612 if (!changed) {
613 return;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800614 }
615 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700616 } finally {
617 writeAccountInfoLocked();
618 writeStatusLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800619 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800620 }
621
622 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
623 }
624
625 public void addPeriodicSync(Account account, String providerName, Bundle extras,
626 long pollFrequency) {
627 updateOrRemovePeriodicSync(account, providerName, extras, pollFrequency, true /* add */);
628 }
629
630 public void removePeriodicSync(Account account, String providerName, Bundle extras) {
631 updateOrRemovePeriodicSync(account, providerName, extras, 0 /* period, ignored */,
632 false /* remove */);
633 }
634
635 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
636 ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
637 synchronized (mAuthorities) {
638 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getPeriodicSyncs");
639 if (authority != null) {
640 for (Pair<Bundle, Long> item : authority.periodicSyncs) {
641 syncs.add(new PeriodicSync(account, providerName, item.first, item.second));
642 }
643 }
644 }
645 return syncs;
646 }
647
Fred Quintanaac9385e2009-06-22 18:00:59 -0700648 public void setMasterSyncAutomatically(boolean flag) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700649 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700650 if (mMasterSyncAutomatically == flag) {
651 return;
652 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700653 mMasterSyncAutomatically = flag;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700654 writeAccountInfoLocked();
655 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700656 if (flag) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800657 ContentResolver.requestSync(null, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700658 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700659 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
660 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700661 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662
Fred Quintanaac9385e2009-06-22 18:00:59 -0700663 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700664 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700665 return mMasterSyncAutomatically;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700666 }
667 }
Costin Manolache360e4542009-09-04 13:36:04 -0700668
Fred Quintana1bbcd102010-02-10 10:04:33 -0800669 public AuthorityInfo getOrCreateAuthority(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700670 synchronized (mAuthorities) {
Fred Quintana1bbcd102010-02-10 10:04:33 -0800671 return getOrCreateAuthorityLocked(account, authority,
672 -1 /* assign a new identifier if creating a new authority */,
673 true /* write to storage if this results in a change */);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700674 }
675 }
Costin Manolache360e4542009-09-04 13:36:04 -0700676
Fred Quintana7620f1a2010-03-16 15:58:44 -0700677 public void removeAuthority(Account account, String authority) {
678 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700679 removeAuthorityLocked(account, authority, true /* doWrite */);
Fred Quintana7620f1a2010-03-16 15:58:44 -0700680 }
681 }
682
Dianne Hackborn231cc602009-04-27 17:10:36 -0700683 public AuthorityInfo getAuthority(int authorityId) {
684 synchronized (mAuthorities) {
685 return mAuthorities.get(authorityId);
686 }
687 }
Costin Manolache360e4542009-09-04 13:36:04 -0700688
Dianne Hackborn231cc602009-04-27 17:10:36 -0700689 /**
690 * Returns true if there is currently a sync operation for the given
691 * account or authority in the pending list, or actively being processed.
692 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700693 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700694 synchronized (mAuthorities) {
695 int i = mPendingOperations.size();
696 while (i > 0) {
697 i--;
698 // TODO(fredq): this probably shouldn't be considering
699 // pending operations.
700 PendingOperation op = mPendingOperations.get(i);
701 if (op.account.equals(account) && op.authority.equals(authority)) {
702 return true;
703 }
704 }
Costin Manolache360e4542009-09-04 13:36:04 -0700705
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700706 if (mCurrentSync != null) {
707 AuthorityInfo ainfo = getAuthority(mCurrentSync.authorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700708 if (ainfo != null && ainfo.account.equals(account)
709 && ainfo.authority.equals(authority)) {
710 return true;
711 }
712 }
713 }
Costin Manolache360e4542009-09-04 13:36:04 -0700714
Dianne Hackborn231cc602009-04-27 17:10:36 -0700715 return false;
716 }
Costin Manolache360e4542009-09-04 13:36:04 -0700717
Dianne Hackborn231cc602009-04-27 17:10:36 -0700718 public PendingOperation insertIntoPending(PendingOperation op) {
719 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700720 if (Log.isLoggable(TAG, Log.VERBOSE)) {
721 Log.v(TAG, "insertIntoPending: account=" + op.account
Dianne Hackborn231cc602009-04-27 17:10:36 -0700722 + " auth=" + op.authority
723 + " src=" + op.syncSource
724 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700725 }
Costin Manolache360e4542009-09-04 13:36:04 -0700726
Dianne Hackborn231cc602009-04-27 17:10:36 -0700727 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
728 op.authority,
729 -1 /* desired identifier */,
730 true /* write accounts to storage */);
731 if (authority == null) {
732 return null;
733 }
Costin Manolache360e4542009-09-04 13:36:04 -0700734
Dianne Hackborn231cc602009-04-27 17:10:36 -0700735 op = new PendingOperation(op);
736 op.authorityId = authority.ident;
737 mPendingOperations.add(op);
738 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700739
Dianne Hackborn231cc602009-04-27 17:10:36 -0700740 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
741 status.pending = true;
742 }
Costin Manolache360e4542009-09-04 13:36:04 -0700743
Fred Quintanaac9385e2009-06-22 18:00:59 -0700744 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700745 return op;
746 }
747
748 public boolean deleteFromPending(PendingOperation op) {
749 boolean res = false;
750 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700751 if (Log.isLoggable(TAG, Log.VERBOSE)) {
752 Log.v(TAG, "deleteFromPending: account=" + op.account
Dianne Hackborn231cc602009-04-27 17:10:36 -0700753 + " auth=" + op.authority
754 + " src=" + op.syncSource
755 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700756 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700757 if (mPendingOperations.remove(op)) {
758 if (mPendingOperations.size() == 0
759 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
760 writePendingOperationsLocked();
761 mNumPendingFinished = 0;
762 } else {
763 mNumPendingFinished++;
764 }
Costin Manolache360e4542009-09-04 13:36:04 -0700765
Dianne Hackborn231cc602009-04-27 17:10:36 -0700766 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
767 "deleteFromPending");
768 if (authority != null) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700769 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700770 final int N = mPendingOperations.size();
771 boolean morePending = false;
772 for (int i=0; i<N; i++) {
773 PendingOperation cur = mPendingOperations.get(i);
774 if (cur.account.equals(op.account)
775 && cur.authority.equals(op.authority)) {
776 morePending = true;
777 break;
778 }
779 }
Costin Manolache360e4542009-09-04 13:36:04 -0700780
Dianne Hackborn231cc602009-04-27 17:10:36 -0700781 if (!morePending) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700782 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700783 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
784 status.pending = false;
785 }
786 }
Costin Manolache360e4542009-09-04 13:36:04 -0700787
Dianne Hackborn231cc602009-04-27 17:10:36 -0700788 res = true;
789 }
790 }
Costin Manolache360e4542009-09-04 13:36:04 -0700791
Fred Quintanaac9385e2009-06-22 18:00:59 -0700792 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700793 return res;
794 }
795
796 public int clearPending() {
797 int num;
798 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700799 if (Log.isLoggable(TAG, Log.VERBOSE)) {
800 Log.v(TAG, "clearPending");
801 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700802 num = mPendingOperations.size();
803 mPendingOperations.clear();
804 final int N = mSyncStatus.size();
805 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700806 mSyncStatus.valueAt(i).pending = false;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700807 }
808 writePendingOperationsLocked();
809 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700810 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700811 return num;
812 }
813
814 /**
815 * Return a copy of the current array of pending operations. The
816 * PendingOperation objects are the real objects stored inside, so that
817 * they can be used with deleteFromPending().
818 */
819 public ArrayList<PendingOperation> getPendingOperations() {
820 synchronized (mAuthorities) {
821 return new ArrayList<PendingOperation>(mPendingOperations);
822 }
823 }
Costin Manolache360e4542009-09-04 13:36:04 -0700824
Dianne Hackborn231cc602009-04-27 17:10:36 -0700825 /**
826 * Return the number of currently pending operations.
827 */
828 public int getPendingOperationCount() {
829 synchronized (mAuthorities) {
830 return mPendingOperations.size();
831 }
832 }
Costin Manolache360e4542009-09-04 13:36:04 -0700833
Dianne Hackborn231cc602009-04-27 17:10:36 -0700834 /**
835 * Called when the set of account has changed, given the new array of
836 * active accounts.
837 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700838 public void doDatabaseCleanup(Account[] accounts) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700839 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700840 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts...");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700841 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
842 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
843 while (accIt.hasNext()) {
844 AccountInfo acc = accIt.next();
845 if (!ArrayUtils.contains(accounts, acc.account)) {
846 // This account no longer exists...
Fred Quintana77c560f2010-03-29 22:20:26 -0700847 if (Log.isLoggable(TAG, Log.VERBOSE)) {
848 Log.w(TAG, "Account removed: " + acc.account);
849 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700850 for (AuthorityInfo auth : acc.authorities.values()) {
851 removing.put(auth.ident, auth);
852 }
853 accIt.remove();
854 }
855 }
Costin Manolache360e4542009-09-04 13:36:04 -0700856
Dianne Hackborn231cc602009-04-27 17:10:36 -0700857 // Clean out all data structures.
858 int i = removing.size();
859 if (i > 0) {
860 while (i > 0) {
861 i--;
862 int ident = removing.keyAt(i);
863 mAuthorities.remove(ident);
864 int j = mSyncStatus.size();
865 while (j > 0) {
866 j--;
867 if (mSyncStatus.keyAt(j) == ident) {
868 mSyncStatus.remove(mSyncStatus.keyAt(j));
869 }
870 }
871 j = mSyncHistory.size();
872 while (j > 0) {
873 j--;
874 if (mSyncHistory.get(j).authorityId == ident) {
875 mSyncHistory.remove(j);
876 }
877 }
878 }
879 writeAccountInfoLocked();
880 writeStatusLocked();
881 writePendingOperationsLocked();
882 writeStatisticsLocked();
883 }
884 }
885 }
886
887 /**
888 * Called when the currently active sync is changing (there can only be
889 * one at a time). Either supply a valid ActiveSyncContext with information
890 * about the sync, or null to stop the currently active sync.
891 */
892 public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
893 synchronized (mAuthorities) {
894 if (activeSyncContext != null) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700895 if (Log.isLoggable(TAG, Log.VERBOSE)) {
896 Log.v(TAG, "setActiveSync: account="
Dianne Hackborn231cc602009-04-27 17:10:36 -0700897 + activeSyncContext.mSyncOperation.account
898 + " auth=" + activeSyncContext.mSyncOperation.authority
899 + " src=" + activeSyncContext.mSyncOperation.syncSource
900 + " extras=" + activeSyncContext.mSyncOperation.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700901 }
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700902 if (mCurrentSync != null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700903 Log.w(TAG, "setActiveSync called with existing active sync!");
904 }
905 AuthorityInfo authority = getAuthorityLocked(
906 activeSyncContext.mSyncOperation.account,
907 activeSyncContext.mSyncOperation.authority,
908 "setActiveSync");
909 if (authority == null) {
910 return;
911 }
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700912 mCurrentSync = new SyncInfo(authority.ident,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700913 authority.account, authority.authority,
914 activeSyncContext.mStartTime);
915 } else {
Fred Quintana77c560f2010-03-29 22:20:26 -0700916 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "setActiveSync: null");
Fred Quintanad5e4fdc2010-03-30 15:16:21 -0700917 mCurrentSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700918 }
919 }
Costin Manolache360e4542009-09-04 13:36:04 -0700920
Fred Quintanaac9385e2009-06-22 18:00:59 -0700921 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700922 }
923
924 /**
925 * To allow others to send active change reports, to poke clients.
926 */
927 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700928 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700929 }
Costin Manolache360e4542009-09-04 13:36:04 -0700930
Dianne Hackborn231cc602009-04-27 17:10:36 -0700931 /**
932 * Note that sync has started for the given account and authority.
933 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700934 public long insertStartSyncEvent(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700935 long now, int source) {
936 long id;
937 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700938 if (Log.isLoggable(TAG, Log.VERBOSE)) {
939 Log.v(TAG, "insertStartSyncEvent: account=" + accountName
Dianne Hackborn231cc602009-04-27 17:10:36 -0700940 + " auth=" + authorityName + " source=" + source);
Fred Quintana77c560f2010-03-29 22:20:26 -0700941 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700942 AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
943 "insertStartSyncEvent");
944 if (authority == null) {
945 return -1;
946 }
947 SyncHistoryItem item = new SyncHistoryItem();
948 item.authorityId = authority.ident;
949 item.historyId = mNextHistoryId++;
950 if (mNextHistoryId < 0) mNextHistoryId = 0;
951 item.eventTime = now;
952 item.source = source;
953 item.event = EVENT_START;
954 mSyncHistory.add(0, item);
955 while (mSyncHistory.size() > MAX_HISTORY) {
956 mSyncHistory.remove(mSyncHistory.size()-1);
957 }
958 id = item.historyId;
Fred Quintana77c560f2010-03-29 22:20:26 -0700959 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700960 }
Costin Manolache360e4542009-09-04 13:36:04 -0700961
Fred Quintanaac9385e2009-06-22 18:00:59 -0700962 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700963 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 }
965
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800966 public static boolean equals(Bundle b1, Bundle b2) {
967 if (b1.size() != b2.size()) {
968 return false;
969 }
970 if (b1.isEmpty()) {
971 return true;
972 }
973 for (String key : b1.keySet()) {
974 if (!b2.containsKey(key)) {
975 return false;
976 }
977 if (!b1.get(key).equals(b2.get(key))) {
978 return false;
979 }
980 }
981 return true;
982 }
983
Fred Quintana77c560f2010-03-29 22:20:26 -0700984 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700986 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700987 if (Log.isLoggable(TAG, Log.VERBOSE)) {
988 Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
989 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700990 SyncHistoryItem item = null;
991 int i = mSyncHistory.size();
992 while (i > 0) {
993 i--;
994 item = mSyncHistory.get(i);
995 if (item.historyId == historyId) {
996 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700998 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 }
Costin Manolache360e4542009-09-04 13:36:04 -07001000
Dianne Hackborn231cc602009-04-27 17:10:36 -07001001 if (item == null) {
1002 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
1003 return;
1004 }
Costin Manolache360e4542009-09-04 13:36:04 -07001005
Dianne Hackborn231cc602009-04-27 17:10:36 -07001006 item.elapsedTime = elapsedTime;
1007 item.event = EVENT_STOP;
1008 item.mesg = resultMessage;
1009 item.downstreamActivity = downstreamActivity;
1010 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -07001011
Dianne Hackborn231cc602009-04-27 17:10:36 -07001012 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -07001013
Dianne Hackborn231cc602009-04-27 17:10:36 -07001014 status.numSyncs++;
1015 status.totalElapsedTime += elapsedTime;
1016 switch (item.source) {
1017 case SOURCE_LOCAL:
1018 status.numSourceLocal++;
1019 break;
1020 case SOURCE_POLL:
1021 status.numSourcePoll++;
1022 break;
1023 case SOURCE_USER:
1024 status.numSourceUser++;
1025 break;
1026 case SOURCE_SERVER:
1027 status.numSourceServer++;
1028 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001029 case SOURCE_PERIODIC:
1030 status.numSourcePeriodic++;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001031 break;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001032 }
Costin Manolache360e4542009-09-04 13:36:04 -07001033
Dianne Hackborn231cc602009-04-27 17:10:36 -07001034 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -07001035 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001036 if (mDayStats[0] == null) {
1037 mDayStats[0] = new DayStats(day);
1038 } else if (day != mDayStats[0].day) {
1039 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
1040 mDayStats[0] = new DayStats(day);
1041 writeStatisticsNow = true;
1042 } else if (mDayStats[0] == null) {
1043 }
1044 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -07001045
Dianne Hackborn231cc602009-04-27 17:10:36 -07001046 final long lastSyncTime = (item.eventTime + elapsedTime);
1047 boolean writeStatusNow = false;
1048 if (MESG_SUCCESS.equals(resultMessage)) {
1049 // - if successful, update the successful columns
1050 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
1051 writeStatusNow = true;
1052 }
1053 status.lastSuccessTime = lastSyncTime;
1054 status.lastSuccessSource = item.source;
1055 status.lastFailureTime = 0;
1056 status.lastFailureSource = -1;
1057 status.lastFailureMesg = null;
1058 status.initialFailureTime = 0;
1059 ds.successCount++;
1060 ds.successTime += elapsedTime;
1061 } else if (!MESG_CANCELED.equals(resultMessage)) {
1062 if (status.lastFailureTime == 0) {
1063 writeStatusNow = true;
1064 }
1065 status.lastFailureTime = lastSyncTime;
1066 status.lastFailureSource = item.source;
1067 status.lastFailureMesg = resultMessage;
1068 if (status.initialFailureTime == 0) {
1069 status.initialFailureTime = lastSyncTime;
1070 }
1071 ds.failureCount++;
1072 ds.failureTime += elapsedTime;
1073 }
Costin Manolache360e4542009-09-04 13:36:04 -07001074
Dianne Hackborn231cc602009-04-27 17:10:36 -07001075 if (writeStatusNow) {
1076 writeStatusLocked();
1077 } else if (!hasMessages(MSG_WRITE_STATUS)) {
1078 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
1079 WRITE_STATUS_DELAY);
1080 }
1081 if (writeStatisticsNow) {
1082 writeStatisticsLocked();
1083 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
1084 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
1085 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -07001086 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001087 }
Costin Manolache360e4542009-09-04 13:36:04 -07001088
Fred Quintanaac9385e2009-06-22 18:00:59 -07001089 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001090 }
1091
1092 /**
1093 * Return the currently active sync information, or null if there is no
1094 * active sync. Note that the returned object is the real, live active
1095 * sync object, so be careful what you do with it.
1096 */
Fred Quintanad5e4fdc2010-03-30 15:16:21 -07001097 public SyncInfo getCurrentSync() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001098 synchronized (mAuthorities) {
Fred Quintanad5e4fdc2010-03-30 15:16:21 -07001099 return mCurrentSync;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001100 }
1101 }
Costin Manolache360e4542009-09-04 13:36:04 -07001102
Dianne Hackborn231cc602009-04-27 17:10:36 -07001103 /**
1104 * Return an array of the current sync status for all authorities. Note
1105 * that the objects inside the array are the real, live status objects,
1106 * so be careful what you do with them.
1107 */
1108 public ArrayList<SyncStatusInfo> getSyncStatus() {
1109 synchronized (mAuthorities) {
1110 final int N = mSyncStatus.size();
1111 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
1112 for (int i=0; i<N; i++) {
1113 ops.add(mSyncStatus.valueAt(i));
1114 }
1115 return ops;
1116 }
1117 }
Costin Manolache360e4542009-09-04 13:36:04 -07001118
Dianne Hackborn231cc602009-04-27 17:10:36 -07001119 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001120 * Return an array of the current authorities. Note
1121 * that the objects inside the array are the real, live objects,
1122 * so be careful what you do with them.
1123 */
1124 public ArrayList<AuthorityInfo> getAuthorities() {
1125 synchronized (mAuthorities) {
1126 final int N = mAuthorities.size();
1127 ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
1128 for (int i=0; i<N; i++) {
1129 infos.add(mAuthorities.valueAt(i));
1130 }
1131 return infos;
1132 }
1133 }
1134
1135 /**
Costin Manolacheb7520982009-09-02 18:03:05 -07001136 * Returns the status that matches the authority and account.
1137 *
1138 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -07001139 * @param authority the authority whose row should be selected
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001140 * @return the SyncStatusInfo for the authority
Dianne Hackborn231cc602009-04-27 17:10:36 -07001141 */
Costin Manolacheb7520982009-09-02 18:03:05 -07001142 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
1143 if (account == null || authority == null) {
1144 throw new IllegalArgumentException();
1145 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001146 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001147 final int N = mSyncStatus.size();
1148 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001149 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001150 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -07001151
1152 if (ainfo != null && ainfo.authority.equals(authority) &&
1153 account.equals(ainfo.account)) {
1154 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001155 }
1156 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001157 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001158 }
1159 }
Costin Manolache360e4542009-09-04 13:36:04 -07001160
Dianne Hackborn231cc602009-04-27 17:10:36 -07001161 /**
1162 * Return true if the pending status is true of any matching authorities.
1163 */
Fred Quintanaac9385e2009-06-22 18:00:59 -07001164 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001165 synchronized (mAuthorities) {
1166 final int N = mSyncStatus.size();
1167 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001168 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001169 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
1170 if (ainfo == null) {
1171 continue;
1172 }
1173 if (account != null && !ainfo.account.equals(account)) {
1174 continue;
1175 }
1176 if (ainfo.authority.equals(authority) && cur.pending) {
1177 return true;
1178 }
1179 }
1180 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 }
1182 }
1183
1184 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001185 * Return an array of the current sync status for all authorities. Note
1186 * that the objects inside the array are the real, live status objects,
1187 * so be careful what you do with them.
1188 */
1189 public ArrayList<SyncHistoryItem> getSyncHistory() {
1190 synchronized (mAuthorities) {
1191 final int N = mSyncHistory.size();
1192 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1193 for (int i=0; i<N; i++) {
1194 items.add(mSyncHistory.get(i));
1195 }
1196 return items;
1197 }
1198 }
Costin Manolache360e4542009-09-04 13:36:04 -07001199
Dianne Hackborn231cc602009-04-27 17:10:36 -07001200 /**
1201 * Return an array of the current per-day statistics. Note
1202 * that the objects inside the array are the real, live status objects,
1203 * so be careful what you do with them.
1204 */
1205 public DayStats[] getDayStatistics() {
1206 synchronized (mAuthorities) {
1207 DayStats[] ds = new DayStats[mDayStats.length];
1208 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1209 return ds;
1210 }
1211 }
Costin Manolache360e4542009-09-04 13:36:04 -07001212
Dianne Hackborn231cc602009-04-27 17:10:36 -07001213 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 * If sync is failing for any of the provider/accounts then determine the time at which it
1215 * started failing and return the earliest time over all the provider/accounts. If none are
1216 * failing then return 0.
1217 */
1218 public long getInitialSyncFailureTime() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001219 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001220 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001221 return 0;
1222 }
Costin Manolache360e4542009-09-04 13:36:04 -07001223
Dianne Hackborn231cc602009-04-27 17:10:36 -07001224 long oldest = 0;
1225 int i = mSyncStatus.size();
1226 while (i > 0) {
1227 i--;
1228 SyncStatusInfo stats = mSyncStatus.valueAt(i);
1229 AuthorityInfo authority = mAuthorities.get(stats.authorityId);
1230 if (authority != null && authority.enabled) {
1231 if (oldest == 0 || stats.initialFailureTime < oldest) {
1232 oldest = stats.initialFailureTime;
1233 }
1234 }
1235 }
Costin Manolache360e4542009-09-04 13:36:04 -07001236
Dianne Hackborn231cc602009-04-27 17:10:36 -07001237 return oldest;
1238 }
1239 }
Costin Manolache360e4542009-09-04 13:36:04 -07001240
Dianne Hackborn55280a92009-05-07 15:53:46 -07001241 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001242 mCal.setTimeInMillis(System.currentTimeMillis());
1243 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1244 if (mYear != mCal.get(Calendar.YEAR)) {
1245 mYear = mCal.get(Calendar.YEAR);
1246 mCal.clear();
1247 mCal.set(Calendar.YEAR, mYear);
1248 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1249 }
1250 return dayOfYear + mYearInDays;
1251 }
Costin Manolache360e4542009-09-04 13:36:04 -07001252
Dianne Hackborn231cc602009-04-27 17:10:36 -07001253 /**
1254 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001255 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001256 * @param accountName The name of the account for the authority.
1257 * @param authorityName The name of the authority itself.
1258 * @param tag If non-null, this will be used in a log message if the
1259 * requested authority does not exist.
1260 */
Dianne Hackborn7a135592009-05-06 00:28:37 -07001261 private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001262 String tag) {
1263 AccountInfo account = mAccounts.get(accountName);
1264 if (account == null) {
1265 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001266 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1267 Log.v(TAG, tag + ": unknown account " + accountName);
1268 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001269 }
1270 return null;
1271 }
1272 AuthorityInfo authority = account.authorities.get(authorityName);
1273 if (authority == null) {
1274 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001275 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1276 Log.v(TAG, tag + ": unknown authority " + authorityName);
1277 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001278 }
1279 return null;
1280 }
Costin Manolache360e4542009-09-04 13:36:04 -07001281
Dianne Hackborn231cc602009-04-27 17:10:36 -07001282 return authority;
1283 }
Costin Manolache360e4542009-09-04 13:36:04 -07001284
Dianne Hackborn7a135592009-05-06 00:28:37 -07001285 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001286 String authorityName, int ident, boolean doWrite) {
1287 AccountInfo account = mAccounts.get(accountName);
1288 if (account == null) {
1289 account = new AccountInfo(accountName);
1290 mAccounts.put(accountName, account);
1291 }
1292 AuthorityInfo authority = account.authorities.get(authorityName);
1293 if (authority == null) {
1294 if (ident < 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001295 ident = mNextAuthorityId;
1296 mNextAuthorityId++;
1297 doWrite = true;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001298 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001299 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1300 Log.v(TAG, "created a new AuthorityInfo for " + accountName
Fred Quintanab763ab22009-08-18 18:07:30 -07001301 + ", provider " + authorityName);
Fred Quintana77c560f2010-03-29 22:20:26 -07001302 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001303 authority = new AuthorityInfo(accountName, authorityName, ident);
1304 account.authorities.put(authorityName, authority);
1305 mAuthorities.put(ident, authority);
1306 if (doWrite) {
1307 writeAccountInfoLocked();
1308 }
1309 }
Costin Manolache360e4542009-09-04 13:36:04 -07001310
Dianne Hackborn231cc602009-04-27 17:10:36 -07001311 return authority;
1312 }
Costin Manolache360e4542009-09-04 13:36:04 -07001313
Fred Quintana77c560f2010-03-29 22:20:26 -07001314 private void removeAuthorityLocked(Account account, String authorityName, boolean doWrite) {
Fred Quintana7620f1a2010-03-16 15:58:44 -07001315 AccountInfo accountInfo = mAccounts.get(account);
1316 if (accountInfo != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001317 final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
1318 if (authorityInfo != null) {
1319 mAuthorities.remove(authorityInfo.ident);
Fred Quintana77c560f2010-03-29 22:20:26 -07001320 if (doWrite) {
1321 writeAccountInfoLocked();
1322 }
Fred Quintana7620f1a2010-03-16 15:58:44 -07001323 }
1324 }
1325 }
1326
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001327 public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
1328 synchronized (mAuthorities) {
1329 return getOrCreateSyncStatusLocked(authority.ident);
1330 }
1331 }
1332
Dianne Hackborn231cc602009-04-27 17:10:36 -07001333 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1334 SyncStatusInfo status = mSyncStatus.get(authorityId);
1335 if (status == null) {
1336 status = new SyncStatusInfo(authorityId);
1337 mSyncStatus.put(authorityId, status);
1338 }
1339 return status;
1340 }
Costin Manolache360e4542009-09-04 13:36:04 -07001341
Dianne Hackborn55280a92009-05-07 15:53:46 -07001342 public void writeAllState() {
1343 synchronized (mAuthorities) {
1344 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001345
Dianne Hackborn55280a92009-05-07 15:53:46 -07001346 if (mNumPendingFinished > 0) {
1347 // Only write these if they are out of date.
1348 writePendingOperationsLocked();
1349 }
Costin Manolache360e4542009-09-04 13:36:04 -07001350
Dianne Hackborn55280a92009-05-07 15:53:46 -07001351 // Just always write these... they are likely out of date.
1352 writeStatusLocked();
1353 writeStatisticsLocked();
1354 }
1355 }
Costin Manolache360e4542009-09-04 13:36:04 -07001356
Dianne Hackborn231cc602009-04-27 17:10:36 -07001357 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001358 * public for testing
1359 */
1360 public void clearAndReadState() {
1361 synchronized (mAuthorities) {
1362 mAuthorities.clear();
1363 mAccounts.clear();
1364 mPendingOperations.clear();
1365 mSyncStatus.clear();
1366 mSyncHistory.clear();
1367
1368 readAccountInfoLocked();
1369 readStatusLocked();
1370 readPendingOperationsLocked();
1371 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -07001372 readAndDeleteLegacyAccountInfoLocked();
1373 writeAccountInfoLocked();
1374 writeStatusLocked();
1375 writePendingOperationsLocked();
1376 writeStatisticsLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001377 }
1378 }
1379
1380 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001381 * Read all account information back in to the initial engine state.
1382 */
1383 private void readAccountInfoLocked() {
Fred Quintana77c560f2010-03-29 22:20:26 -07001384 int highestAuthorityId = -1;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001385 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001387 fis = mAccountInfoFile.openRead();
1388 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1389 XmlPullParser parser = Xml.newPullParser();
1390 parser.setInput(fis, null);
1391 int eventType = parser.getEventType();
1392 while (eventType != XmlPullParser.START_TAG) {
1393 eventType = parser.next();
1394 }
1395 String tagName = parser.getName();
1396 if ("accounts".equals(tagName)) {
1397 String listen = parser.getAttributeValue(
1398 null, "listen-for-tickles");
Fred Quintanac2e46912010-03-15 16:10:44 -07001399 String versionString = parser.getAttributeValue(null, "version");
1400 int version;
1401 try {
1402 version = (versionString == null) ? 0 : Integer.parseInt(versionString);
1403 } catch (NumberFormatException e) {
1404 version = 0;
1405 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001406 String nextIdString = parser.getAttributeValue(null, "nextAuthorityId");
1407 try {
1408 int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
1409 mNextAuthorityId = Math.max(mNextAuthorityId, id);
1410 } catch (NumberFormatException e) {
1411 // don't care
Fred Quintanac2e46912010-03-15 16:10:44 -07001412 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001413 mMasterSyncAutomatically = listen == null || Boolean.parseBoolean(listen);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001414 eventType = parser.next();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001415 AuthorityInfo authority = null;
1416 Pair<Bundle, Long> periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001417 do {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001418 if (eventType == XmlPullParser.START_TAG) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001419 tagName = parser.getName();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001420 if (parser.getDepth() == 2) {
1421 if ("authority".equals(tagName)) {
Fred Quintanac2e46912010-03-15 16:10:44 -07001422 authority = parseAuthority(parser, version);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001423 periodicSync = null;
Fred Quintana77c560f2010-03-29 22:20:26 -07001424 if (authority.ident > highestAuthorityId) {
1425 highestAuthorityId = authority.ident;
1426 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001427 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001428 } else if (parser.getDepth() == 3) {
1429 if ("periodicSync".equals(tagName) && authority != null) {
1430 periodicSync = parsePeriodicSync(parser, authority);
1431 }
1432 } else if (parser.getDepth() == 4 && periodicSync != null) {
1433 if ("extra".equals(tagName)) {
1434 parseExtra(parser, periodicSync);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001435 }
1436 }
1437 }
1438 eventType = parser.next();
1439 } while (eventType != XmlPullParser.END_DOCUMENT);
1440 }
1441 } catch (XmlPullParserException e) {
1442 Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001443 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001444 } catch (java.io.IOException e) {
1445 if (fis == null) Log.i(TAG, "No initial accounts");
1446 else Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001447 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001448 } finally {
Fred Quintana77c560f2010-03-29 22:20:26 -07001449 mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001450 if (fis != null) {
1451 try {
1452 fis.close();
1453 } catch (java.io.IOException e1) {
1454 }
1455 }
1456 }
Fred Quintanac2e46912010-03-15 16:10:44 -07001457
Fred Quintana77c560f2010-03-29 22:20:26 -07001458 maybeMigrateSettingsForRenamedAuthorities();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001459 }
Costin Manolache360e4542009-09-04 13:36:04 -07001460
Fred Quintanafb084402010-03-23 17:57:03 -07001461 /**
1462 * some authority names have changed. copy over their settings and delete the old ones
1463 * @return true if a change was made
1464 */
1465 private boolean maybeMigrateSettingsForRenamedAuthorities() {
1466 boolean writeNeeded = false;
1467
1468 ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
1469 final int N = mAuthorities.size();
1470 for (int i=0; i<N; i++) {
1471 AuthorityInfo authority = mAuthorities.valueAt(i);
1472 // skip this authority if it isn't one of the renamed ones
1473 final String newAuthorityName = sAuthorityRenames.get(authority.authority);
1474 if (newAuthorityName == null) {
1475 continue;
1476 }
1477
1478 // remember this authority so we can remove it later. we can't remove it
1479 // now without messing up this loop iteration
1480 authoritiesToRemove.add(authority);
1481
1482 // this authority isn't enabled, no need to copy it to the new authority name since
1483 // the default is "disabled"
1484 if (!authority.enabled) {
1485 continue;
1486 }
1487
1488 // if we already have a record of this new authority then don't copy over the settings
1489 if (getAuthorityLocked(authority.account, newAuthorityName, "cleanup") != null) {
1490 continue;
1491 }
1492
1493 AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
1494 newAuthorityName, -1 /* ident */, false /* doWrite */);
1495 newAuthority.enabled = true;
1496 writeNeeded = true;
1497 }
1498
1499 for (AuthorityInfo authorityInfo : authoritiesToRemove) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001500 removeAuthorityLocked(authorityInfo.account, authorityInfo.authority,
1501 false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001502 writeNeeded = true;
1503 }
1504
1505 return writeNeeded;
1506 }
1507
Fred Quintanac2e46912010-03-15 16:10:44 -07001508 private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001509 AuthorityInfo authority = null;
1510 int id = -1;
1511 try {
1512 id = Integer.parseInt(parser.getAttributeValue(
1513 null, "id"));
1514 } catch (NumberFormatException e) {
1515 Log.e(TAG, "error parsing the id of the authority", e);
1516 } catch (NullPointerException e) {
1517 Log.e(TAG, "the id of the authority is null", e);
1518 }
1519 if (id >= 0) {
Fred Quintanafb084402010-03-23 17:57:03 -07001520 String authorityName = parser.getAttributeValue(null, "authority");
1521 String enabled = parser.getAttributeValue(null, "enabled");
1522 String syncable = parser.getAttributeValue(null, "syncable");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001523 String accountName = parser.getAttributeValue(null, "account");
1524 String accountType = parser.getAttributeValue(null, "type");
1525 if (accountType == null) {
1526 accountType = "com.google";
Fred Quintanafb084402010-03-23 17:57:03 -07001527 syncable = "unknown";
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001528 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001529 authority = mAuthorities.get(id);
1530 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1531 + accountName + " auth=" + authorityName
1532 + " enabled=" + enabled
1533 + " syncable=" + syncable);
1534 if (authority == null) {
1535 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1536 authority = getOrCreateAuthorityLocked(
1537 new Account(accountName, accountType), authorityName, id, false);
Fred Quintanac2e46912010-03-15 16:10:44 -07001538 // If the version is 0 then we are upgrading from a file format that did not
1539 // know about periodic syncs. In that case don't clear the list since we
1540 // want the default, which is a daily periodioc sync.
1541 // Otherwise clear out this default list since we will populate it later with
1542 // the periodic sync descriptions that are read from the configuration file.
1543 if (version > 0) {
1544 authority.periodicSyncs.clear();
1545 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001546 }
1547 if (authority != null) {
1548 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
1549 if ("unknown".equals(syncable)) {
1550 authority.syncable = -1;
1551 } else {
1552 authority.syncable =
Fred Quintanafb084402010-03-23 17:57:03 -07001553 (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001554 }
1555 } else {
1556 Log.w(TAG, "Failure adding authority: account="
1557 + accountName + " auth=" + authorityName
1558 + " enabled=" + enabled
1559 + " syncable=" + syncable);
1560 }
1561 }
1562
1563 return authority;
1564 }
1565
1566 private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
1567 Bundle extras = new Bundle();
1568 String periodValue = parser.getAttributeValue(null, "period");
1569 final long period;
1570 try {
1571 period = Long.parseLong(periodValue);
1572 } catch (NumberFormatException e) {
1573 Log.e(TAG, "error parsing the period of a periodic sync", e);
1574 return null;
1575 } catch (NullPointerException e) {
1576 Log.e(TAG, "the period of a periodic sync is null", e);
1577 return null;
1578 }
1579 final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
1580 authority.periodicSyncs.add(periodicSync);
Fred Quintanac2e46912010-03-15 16:10:44 -07001581
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001582 return periodicSync;
1583 }
1584
1585 private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
1586 final Bundle extras = periodicSync.first;
1587 String name = parser.getAttributeValue(null, "name");
1588 String type = parser.getAttributeValue(null, "type");
1589 String value1 = parser.getAttributeValue(null, "value1");
1590 String value2 = parser.getAttributeValue(null, "value2");
1591
1592 try {
1593 if ("long".equals(type)) {
1594 extras.putLong(name, Long.parseLong(value1));
1595 } else if ("integer".equals(type)) {
1596 extras.putInt(name, Integer.parseInt(value1));
1597 } else if ("double".equals(type)) {
1598 extras.putDouble(name, Double.parseDouble(value1));
1599 } else if ("float".equals(type)) {
1600 extras.putFloat(name, Float.parseFloat(value1));
1601 } else if ("boolean".equals(type)) {
1602 extras.putBoolean(name, Boolean.parseBoolean(value1));
1603 } else if ("string".equals(type)) {
1604 extras.putString(name, value1);
1605 } else if ("account".equals(type)) {
1606 extras.putParcelable(name, new Account(value1, value2));
1607 }
1608 } catch (NumberFormatException e) {
1609 Log.e(TAG, "error parsing bundle value", e);
1610 } catch (NullPointerException e) {
1611 Log.e(TAG, "error parsing bundle value", e);
1612 }
1613 }
1614
Dianne Hackborn231cc602009-04-27 17:10:36 -07001615 /**
1616 * Write all account information to the account file.
1617 */
1618 private void writeAccountInfoLocked() {
1619 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1620 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001621
Dianne Hackborn231cc602009-04-27 17:10:36 -07001622 try {
1623 fos = mAccountInfoFile.startWrite();
1624 XmlSerializer out = new FastXmlSerializer();
1625 out.setOutput(fos, "utf-8");
1626 out.startDocument(null, true);
1627 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001628
Dianne Hackborn231cc602009-04-27 17:10:36 -07001629 out.startTag(null, "accounts");
Fred Quintanac2e46912010-03-15 16:10:44 -07001630 out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
Fred Quintana77c560f2010-03-29 22:20:26 -07001631 out.attribute(null, "nextAuthorityId", Integer.toString(mNextAuthorityId));
Fred Quintanaac9385e2009-06-22 18:00:59 -07001632 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001633 out.attribute(null, "listen-for-tickles", "false");
1634 }
Costin Manolache360e4542009-09-04 13:36:04 -07001635
Dianne Hackborn231cc602009-04-27 17:10:36 -07001636 final int N = mAuthorities.size();
1637 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001638 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001639 out.startTag(null, "authority");
1640 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001641 out.attribute(null, "account", authority.account.name);
1642 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001643 out.attribute(null, "authority", authority.authority);
Fred Quintanafb084402010-03-23 17:57:03 -07001644 out.attribute(null, "enabled", Boolean.toString(authority.enabled));
Fred Quintana5e787c42009-08-16 23:13:53 -07001645 if (authority.syncable < 0) {
1646 out.attribute(null, "syncable", "unknown");
Fred Quintanafb084402010-03-23 17:57:03 -07001647 } else {
1648 out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
Fred Quintana5e787c42009-08-16 23:13:53 -07001649 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001650 for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
1651 out.startTag(null, "periodicSync");
1652 out.attribute(null, "period", Long.toString(periodicSync.second));
1653 final Bundle extras = periodicSync.first;
1654 for (String key : extras.keySet()) {
1655 out.startTag(null, "extra");
1656 out.attribute(null, "name", key);
1657 final Object value = extras.get(key);
1658 if (value instanceof Long) {
1659 out.attribute(null, "type", "long");
1660 out.attribute(null, "value1", value.toString());
1661 } else if (value instanceof Integer) {
1662 out.attribute(null, "type", "integer");
1663 out.attribute(null, "value1", value.toString());
1664 } else if (value instanceof Boolean) {
1665 out.attribute(null, "type", "boolean");
1666 out.attribute(null, "value1", value.toString());
1667 } else if (value instanceof Float) {
1668 out.attribute(null, "type", "float");
1669 out.attribute(null, "value1", value.toString());
1670 } else if (value instanceof Double) {
1671 out.attribute(null, "type", "double");
1672 out.attribute(null, "value1", value.toString());
1673 } else if (value instanceof String) {
1674 out.attribute(null, "type", "string");
1675 out.attribute(null, "value1", value.toString());
1676 } else if (value instanceof Account) {
1677 out.attribute(null, "type", "account");
1678 out.attribute(null, "value1", ((Account)value).name);
1679 out.attribute(null, "value2", ((Account)value).type);
1680 }
1681 out.endTag(null, "extra");
1682 }
1683 out.endTag(null, "periodicSync");
1684 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001685 out.endTag(null, "authority");
1686 }
Costin Manolache360e4542009-09-04 13:36:04 -07001687
Dianne Hackborn231cc602009-04-27 17:10:36 -07001688 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001689
Dianne Hackborn231cc602009-04-27 17:10:36 -07001690 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001691
Dianne Hackborn231cc602009-04-27 17:10:36 -07001692 mAccountInfoFile.finishWrite(fos);
1693 } catch (java.io.IOException e1) {
1694 Log.w(TAG, "Error writing accounts", e1);
1695 if (fos != null) {
1696 mAccountInfoFile.failWrite(fos);
1697 }
1698 }
1699 }
Costin Manolache360e4542009-09-04 13:36:04 -07001700
Dianne Hackborn231cc602009-04-27 17:10:36 -07001701 static int getIntColumn(Cursor c, String name) {
1702 return c.getInt(c.getColumnIndex(name));
1703 }
Costin Manolache360e4542009-09-04 13:36:04 -07001704
Dianne Hackborn231cc602009-04-27 17:10:36 -07001705 static long getLongColumn(Cursor c, String name) {
1706 return c.getLong(c.getColumnIndex(name));
1707 }
Costin Manolache360e4542009-09-04 13:36:04 -07001708
Dianne Hackborn231cc602009-04-27 17:10:36 -07001709 /**
1710 * Load sync engine state from the old syncmanager database, and then
1711 * erase it. Note that we don't deal with pending operations, active
1712 * sync, or history.
1713 */
Fred Quintana77c560f2010-03-29 22:20:26 -07001714 private void readAndDeleteLegacyAccountInfoLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001715 // Look for old database to initialize from.
1716 File file = mContext.getDatabasePath("syncmanager.db");
1717 if (!file.exists()) {
1718 return;
1719 }
1720 String path = file.getPath();
1721 SQLiteDatabase db = null;
1722 try {
1723 db = SQLiteDatabase.openDatabase(path, null,
1724 SQLiteDatabase.OPEN_READONLY);
1725 } catch (SQLiteException e) {
1726 }
Costin Manolache360e4542009-09-04 13:36:04 -07001727
Dianne Hackborn231cc602009-04-27 17:10:36 -07001728 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001729 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001730
Dianne Hackborn231cc602009-04-27 17:10:36 -07001731 // Copy in all of the status information, as well as accounts.
1732 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1733 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1734 qb.setTables("stats, status");
1735 HashMap<String,String> map = new HashMap<String,String>();
1736 map.put("_id", "status._id as _id");
1737 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001738 if (hasType) {
1739 map.put("account_type", "stats.account_type as account_type");
1740 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001741 map.put("authority", "stats.authority as authority");
1742 map.put("totalElapsedTime", "totalElapsedTime");
1743 map.put("numSyncs", "numSyncs");
1744 map.put("numSourceLocal", "numSourceLocal");
1745 map.put("numSourcePoll", "numSourcePoll");
1746 map.put("numSourceServer", "numSourceServer");
1747 map.put("numSourceUser", "numSourceUser");
1748 map.put("lastSuccessSource", "lastSuccessSource");
1749 map.put("lastSuccessTime", "lastSuccessTime");
1750 map.put("lastFailureSource", "lastFailureSource");
1751 map.put("lastFailureTime", "lastFailureTime");
1752 map.put("lastFailureMesg", "lastFailureMesg");
1753 map.put("pending", "pending");
1754 qb.setProjectionMap(map);
1755 qb.appendWhere("stats._id = status.stats_id");
1756 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001758 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001759 String accountType = hasType
1760 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001761 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001762 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001764 String authorityName = c.getString(c.getColumnIndex("authority"));
1765 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001766 new Account(accountName, accountType),
1767 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001768 if (authority != null) {
1769 int i = mSyncStatus.size();
1770 boolean found = false;
1771 SyncStatusInfo st = null;
1772 while (i > 0) {
1773 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001774 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001775 if (st.authorityId == authority.ident) {
1776 found = true;
1777 break;
1778 }
1779 }
1780 if (!found) {
1781 st = new SyncStatusInfo(authority.ident);
1782 mSyncStatus.put(authority.ident, st);
1783 }
1784 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1785 st.numSyncs = getIntColumn(c, "numSyncs");
1786 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1787 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1788 st.numSourceServer = getIntColumn(c, "numSourceServer");
1789 st.numSourceUser = getIntColumn(c, "numSourceUser");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001790 st.numSourcePeriodic = 0;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001791 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1792 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1793 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1794 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1795 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1796 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 }
Costin Manolache360e4542009-09-04 13:36:04 -07001799
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001801
Dianne Hackborn231cc602009-04-27 17:10:36 -07001802 // Retrieve the settings.
1803 qb = new SQLiteQueryBuilder();
1804 qb.setTables("settings");
1805 c = qb.query(db, null, null, null, null, null, null);
1806 while (c.moveToNext()) {
1807 String name = c.getString(c.getColumnIndex("name"));
1808 String value = c.getString(c.getColumnIndex("value"));
1809 if (name == null) continue;
1810 if (name.equals("listen_for_tickles")) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001811 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001812 } else if (name.startsWith("sync_provider_")) {
1813 String provider = name.substring("sync_provider_".length(),
1814 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001815 int i = mAuthorities.size();
1816 while (i > 0) {
1817 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001818 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001819 if (authority.authority.equals(provider)) {
1820 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001821 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001822 }
1823 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001824 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 }
Costin Manolache360e4542009-09-04 13:36:04 -07001826
Dianne Hackborn231cc602009-04-27 17:10:36 -07001827 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001828
Dianne Hackborn231cc602009-04-27 17:10:36 -07001829 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001830
Dianne Hackborn231cc602009-04-27 17:10:36 -07001831 (new File(path)).delete();
1832 }
1833 }
Costin Manolache360e4542009-09-04 13:36:04 -07001834
Dianne Hackborn231cc602009-04-27 17:10:36 -07001835 public static final int STATUS_FILE_END = 0;
1836 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001837
Dianne Hackborn231cc602009-04-27 17:10:36 -07001838 /**
1839 * Read all sync status back in to the initial engine state.
1840 */
1841 private void readStatusLocked() {
1842 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1843 try {
1844 byte[] data = mStatusFile.readFully();
1845 Parcel in = Parcel.obtain();
1846 in.unmarshall(data, 0, data.length);
1847 in.setDataPosition(0);
1848 int token;
1849 while ((token=in.readInt()) != STATUS_FILE_END) {
1850 if (token == STATUS_FILE_ITEM) {
1851 SyncStatusInfo status = new SyncStatusInfo(in);
1852 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1853 status.pending = false;
1854 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1855 + status.authorityId);
1856 mSyncStatus.put(status.authorityId, status);
1857 }
1858 } else {
1859 // Ooops.
1860 Log.w(TAG, "Unknown status token: " + token);
1861 break;
1862 }
1863 }
1864 } catch (java.io.IOException e) {
1865 Log.i(TAG, "No initial status");
1866 }
1867 }
Costin Manolache360e4542009-09-04 13:36:04 -07001868
Dianne Hackborn231cc602009-04-27 17:10:36 -07001869 /**
1870 * Write all sync status to the sync status file.
1871 */
1872 private void writeStatusLocked() {
1873 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001874
Dianne Hackborn231cc602009-04-27 17:10:36 -07001875 // The file is being written, so we don't need to have a scheduled
1876 // write until the next change.
1877 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001878
Dianne Hackborn231cc602009-04-27 17:10:36 -07001879 FileOutputStream fos = null;
1880 try {
1881 fos = mStatusFile.startWrite();
1882 Parcel out = Parcel.obtain();
1883 final int N = mSyncStatus.size();
1884 for (int i=0; i<N; i++) {
1885 SyncStatusInfo status = mSyncStatus.valueAt(i);
1886 out.writeInt(STATUS_FILE_ITEM);
1887 status.writeToParcel(out, 0);
1888 }
1889 out.writeInt(STATUS_FILE_END);
1890 fos.write(out.marshall());
1891 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001892
Dianne Hackborn231cc602009-04-27 17:10:36 -07001893 mStatusFile.finishWrite(fos);
1894 } catch (java.io.IOException e1) {
1895 Log.w(TAG, "Error writing status", e1);
1896 if (fos != null) {
1897 mStatusFile.failWrite(fos);
1898 }
1899 }
1900 }
Costin Manolache360e4542009-09-04 13:36:04 -07001901
Fred Quintana307da1a2010-01-21 14:24:20 -08001902 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07001903
Dianne Hackborn231cc602009-04-27 17:10:36 -07001904 /**
1905 * Read all pending operations back in to the initial engine state.
1906 */
1907 private void readPendingOperationsLocked() {
1908 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1909 try {
1910 byte[] data = mPendingFile.readFully();
1911 Parcel in = Parcel.obtain();
1912 in.unmarshall(data, 0, data.length);
1913 in.setDataPosition(0);
1914 final int SIZE = in.dataSize();
1915 while (in.dataPosition() < SIZE) {
1916 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08001917 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001918 Log.w(TAG, "Unknown pending operation version "
1919 + version + "; dropping all ops");
1920 break;
1921 }
1922 int authorityId = in.readInt();
1923 int syncSource = in.readInt();
1924 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08001925 boolean expedited;
1926 if (version == PENDING_OPERATION_VERSION) {
1927 expedited = in.readInt() != 0;
1928 } else {
1929 expedited = false;
1930 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001931 AuthorityInfo authority = mAuthorities.get(authorityId);
1932 if (authority != null) {
1933 Bundle extras = null;
1934 if (flatExtras != null) {
1935 extras = unflattenBundle(flatExtras);
1936 }
1937 PendingOperation op = new PendingOperation(
1938 authority.account, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08001939 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001940 op.authorityId = authorityId;
1941 op.flatExtras = flatExtras;
1942 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
1943 + " auth=" + op.authority
1944 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08001945 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07001946 + " extras=" + op.extras);
1947 mPendingOperations.add(op);
1948 }
1949 }
1950 } catch (java.io.IOException e) {
1951 Log.i(TAG, "No initial pending operations");
1952 }
1953 }
Costin Manolache360e4542009-09-04 13:36:04 -07001954
Dianne Hackborn231cc602009-04-27 17:10:36 -07001955 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
1956 out.writeInt(PENDING_OPERATION_VERSION);
1957 out.writeInt(op.authorityId);
1958 out.writeInt(op.syncSource);
1959 if (op.flatExtras == null && op.extras != null) {
1960 op.flatExtras = flattenBundle(op.extras);
1961 }
1962 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08001963 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001964 }
Costin Manolache360e4542009-09-04 13:36:04 -07001965
Dianne Hackborn231cc602009-04-27 17:10:36 -07001966 /**
1967 * Write all currently pending ops to the pending ops file.
1968 */
1969 private void writePendingOperationsLocked() {
1970 final int N = mPendingOperations.size();
1971 FileOutputStream fos = null;
1972 try {
1973 if (N == 0) {
1974 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
1975 mPendingFile.truncate();
1976 return;
1977 }
Costin Manolache360e4542009-09-04 13:36:04 -07001978
Dianne Hackborn231cc602009-04-27 17:10:36 -07001979 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
1980 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07001981
Dianne Hackborn231cc602009-04-27 17:10:36 -07001982 Parcel out = Parcel.obtain();
1983 for (int i=0; i<N; i++) {
1984 PendingOperation op = mPendingOperations.get(i);
1985 writePendingOperationLocked(op, out);
1986 }
1987 fos.write(out.marshall());
1988 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001989
Dianne Hackborn231cc602009-04-27 17:10:36 -07001990 mPendingFile.finishWrite(fos);
1991 } catch (java.io.IOException e1) {
1992 Log.w(TAG, "Error writing pending operations", e1);
1993 if (fos != null) {
1994 mPendingFile.failWrite(fos);
1995 }
1996 }
1997 }
Costin Manolache360e4542009-09-04 13:36:04 -07001998
Dianne Hackborn231cc602009-04-27 17:10:36 -07001999 /**
2000 * Append the given operation to the pending ops file; if unable to,
2001 * write all pending ops.
2002 */
2003 private void appendPendingOperationLocked(PendingOperation op) {
2004 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
2005 FileOutputStream fos = null;
2006 try {
2007 fos = mPendingFile.openAppend();
2008 } catch (java.io.IOException e) {
2009 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
2010 writePendingOperationsLocked();
2011 return;
2012 }
Costin Manolache360e4542009-09-04 13:36:04 -07002013
Dianne Hackborn231cc602009-04-27 17:10:36 -07002014 try {
2015 Parcel out = Parcel.obtain();
2016 writePendingOperationLocked(op, out);
2017 fos.write(out.marshall());
2018 out.recycle();
2019 } catch (java.io.IOException e1) {
2020 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002022 try {
2023 fos.close();
2024 } catch (java.io.IOException e2) {
2025 }
2026 }
2027 }
Costin Manolache360e4542009-09-04 13:36:04 -07002028
Dianne Hackborn231cc602009-04-27 17:10:36 -07002029 static private byte[] flattenBundle(Bundle bundle) {
2030 byte[] flatData = null;
2031 Parcel parcel = Parcel.obtain();
2032 try {
2033 bundle.writeToParcel(parcel, 0);
2034 flatData = parcel.marshall();
2035 } finally {
2036 parcel.recycle();
2037 }
2038 return flatData;
2039 }
Costin Manolache360e4542009-09-04 13:36:04 -07002040
Dianne Hackborn231cc602009-04-27 17:10:36 -07002041 static private Bundle unflattenBundle(byte[] flatData) {
2042 Bundle bundle;
2043 Parcel parcel = Parcel.obtain();
2044 try {
2045 parcel.unmarshall(flatData, 0, flatData.length);
2046 parcel.setDataPosition(0);
2047 bundle = parcel.readBundle();
2048 } catch (RuntimeException e) {
2049 // A RuntimeException is thrown if we were unable to parse the parcel.
2050 // Create an empty parcel in this case.
2051 bundle = new Bundle();
2052 } finally {
2053 parcel.recycle();
2054 }
2055 return bundle;
2056 }
Costin Manolache360e4542009-09-04 13:36:04 -07002057
Dianne Hackborn231cc602009-04-27 17:10:36 -07002058 public static final int STATISTICS_FILE_END = 0;
2059 public static final int STATISTICS_FILE_ITEM_OLD = 100;
2060 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07002061
Dianne Hackborn231cc602009-04-27 17:10:36 -07002062 /**
2063 * Read all sync statistics back in to the initial engine state.
2064 */
2065 private void readStatisticsLocked() {
2066 try {
2067 byte[] data = mStatisticsFile.readFully();
2068 Parcel in = Parcel.obtain();
2069 in.unmarshall(data, 0, data.length);
2070 in.setDataPosition(0);
2071 int token;
2072 int index = 0;
2073 while ((token=in.readInt()) != STATISTICS_FILE_END) {
2074 if (token == STATISTICS_FILE_ITEM
2075 || token == STATISTICS_FILE_ITEM_OLD) {
2076 int day = in.readInt();
2077 if (token == STATISTICS_FILE_ITEM_OLD) {
2078 day = day - 2009 + 14245; // Magic!
2079 }
2080 DayStats ds = new DayStats(day);
2081 ds.successCount = in.readInt();
2082 ds.successTime = in.readLong();
2083 ds.failureCount = in.readInt();
2084 ds.failureTime = in.readLong();
2085 if (index < mDayStats.length) {
2086 mDayStats[index] = ds;
2087 index++;
2088 }
2089 } else {
2090 // Ooops.
2091 Log.w(TAG, "Unknown stats token: " + token);
2092 break;
2093 }
2094 }
2095 } catch (java.io.IOException e) {
2096 Log.i(TAG, "No initial statistics");
2097 }
2098 }
Costin Manolache360e4542009-09-04 13:36:04 -07002099
Dianne Hackborn231cc602009-04-27 17:10:36 -07002100 /**
2101 * Write all sync statistics to the sync status file.
2102 */
2103 private void writeStatisticsLocked() {
2104 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07002105
Dianne Hackborn231cc602009-04-27 17:10:36 -07002106 // The file is being written, so we don't need to have a scheduled
2107 // write until the next change.
2108 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07002109
Dianne Hackborn231cc602009-04-27 17:10:36 -07002110 FileOutputStream fos = null;
2111 try {
2112 fos = mStatisticsFile.startWrite();
2113 Parcel out = Parcel.obtain();
2114 final int N = mDayStats.length;
2115 for (int i=0; i<N; i++) {
2116 DayStats ds = mDayStats[i];
2117 if (ds == null) {
2118 break;
2119 }
2120 out.writeInt(STATISTICS_FILE_ITEM);
2121 out.writeInt(ds.day);
2122 out.writeInt(ds.successCount);
2123 out.writeLong(ds.successTime);
2124 out.writeInt(ds.failureCount);
2125 out.writeLong(ds.failureTime);
2126 }
2127 out.writeInt(STATISTICS_FILE_END);
2128 fos.write(out.marshall());
2129 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002130
Dianne Hackborn231cc602009-04-27 17:10:36 -07002131 mStatisticsFile.finishWrite(fos);
2132 } catch (java.io.IOException e1) {
2133 Log.w(TAG, "Error writing stats", e1);
2134 if (fos != null) {
2135 mStatisticsFile.failWrite(fos);
2136 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002137 }
2138 }
2139}