blob: db70096f535cc6065410f2f02f59f28f4317feee [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;
Tom Taylord4a47292009-12-21 13:59:18 -080021import com.android.common.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;
Fred Quintana307da1a2010-01-21 14:24:20 -080039import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.util.Log;
Dianne Hackborn231cc602009-04-27 17:10:36 -070041import android.util.SparseArray;
42import android.util.Xml;
Fred Quintana307da1a2010-01-21 14:24:20 -080043import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Dianne Hackborn231cc602009-04-27 17:10:36 -070045import java.io.File;
46import java.io.FileInputStream;
47import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import java.util.ArrayList;
Dianne Hackborn231cc602009-04-27 17:10:36 -070049import java.util.Calendar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070051import java.util.Iterator;
52import java.util.TimeZone;
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 = false;
63 private static final boolean DEBUG_FILE = false;
Costin Manolache360e4542009-09-04 13:36:04 -070064
Dianne Hackborn231cc602009-04-27 17:10:36 -070065 // @VisibleForTesting
66 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
Dianne Hackborn231cc602009-04-27 17:10:36 -070068 /** Enum value for a sync start event. */
69 public static final int EVENT_START = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
Dianne Hackborn231cc602009-04-27 17:10:36 -070071 /** Enum value for a sync stop event. */
72 public static final int EVENT_STOP = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
Dianne Hackborn231cc602009-04-27 17:10:36 -070074 // TODO: i18n -- grab these out of resources.
75 /** String names for the sync event types. */
76 public static final String[] EVENTS = { "START", "STOP" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
Dianne Hackborn231cc602009-04-27 17:10:36 -070078 /** Enum value for a server-initiated sync. */
79 public static final int SOURCE_SERVER = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
Dianne Hackborn231cc602009-04-27 17:10:36 -070081 /** Enum value for a local-initiated sync. */
82 public static final int SOURCE_LOCAL = 1;
83 /**
84 * Enum value for a poll-based sync (e.g., upon connection to
85 * network)
86 */
87 public static final int SOURCE_POLL = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
Dianne Hackborn231cc602009-04-27 17:10:36 -070089 /** Enum value for a user-initiated sync. */
90 public static final int SOURCE_USER = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
Fred Quintana307da1a2010-01-21 14:24:20 -080092 public static final long NOT_IN_BACKOFF_MODE = -1;
93
Fred Quintanaac9385e2009-06-22 18:00:59 -070094 private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
95 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
96
Dianne Hackborn231cc602009-04-27 17:10:36 -070097 // TODO: i18n -- grab these out of resources.
98 /** String names for the sync source types. */
99 public static final String[] SOURCES = { "SERVER",
100 "LOCAL",
101 "POLL",
102 "USER" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
Dianne Hackborn231cc602009-04-27 17:10:36 -0700104 // The MESG column will contain one of these or one of the Error types.
105 public static final String MESG_SUCCESS = "success";
106 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700108 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700109
Dianne Hackborn231cc602009-04-27 17:10:36 -0700110 private static final int MSG_WRITE_STATUS = 1;
111 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700112
Dianne Hackborn231cc602009-04-27 17:10:36 -0700113 private static final int MSG_WRITE_STATISTICS = 2;
114 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700115
116 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700117
Dianne Hackborn231cc602009-04-27 17:10:36 -0700118 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700119 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700120 final int syncSource;
121 final String authority;
122 final Bundle extras; // note: read-only.
Fred Quintana307da1a2010-01-21 14:24:20 -0800123 final boolean expedited;
Costin Manolache360e4542009-09-04 13:36:04 -0700124
Dianne Hackborn231cc602009-04-27 17:10:36 -0700125 int authorityId;
126 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700127
Dianne Hackborn7a135592009-05-06 00:28:37 -0700128 PendingOperation(Account account, int source,
Fred Quintana307da1a2010-01-21 14:24:20 -0800129 String authority, Bundle extras, boolean expedited) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700130 this.account = account;
131 this.syncSource = source;
132 this.authority = authority;
133 this.extras = extras != null ? new Bundle(extras) : extras;
Fred Quintana307da1a2010-01-21 14:24:20 -0800134 this.expedited = expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700135 this.authorityId = -1;
136 }
137
138 PendingOperation(PendingOperation other) {
139 this.account = other.account;
140 this.syncSource = other.syncSource;
141 this.authority = other.authority;
142 this.extras = other.extras;
143 this.authorityId = other.authorityId;
Fred Quintana307da1a2010-01-21 14:24:20 -0800144 this.expedited = other.expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 }
Costin Manolache360e4542009-09-04 13:36:04 -0700147
Dianne Hackborn231cc602009-04-27 17:10:36 -0700148 static class AccountInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700149 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700150 final HashMap<String, AuthorityInfo> authorities =
151 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700152
Dianne Hackborn7a135592009-05-06 00:28:37 -0700153 AccountInfo(Account account) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700154 this.account = account;
155 }
156 }
Costin Manolache360e4542009-09-04 13:36:04 -0700157
Dianne Hackborn231cc602009-04-27 17:10:36 -0700158 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700159 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700160 final String authority;
161 final int ident;
162 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700163 int syncable;
Fred Quintana307da1a2010-01-21 14:24:20 -0800164 long backoffTime;
165 long backoffDelay;
166 long delayUntil;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700167
Dianne Hackborn7a135592009-05-06 00:28:37 -0700168 AuthorityInfo(Account account, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700169 this.account = account;
170 this.authority = authority;
171 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700172 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700173 syncable = -1; // default to "unknown"
Fred Quintana307da1a2010-01-21 14:24:20 -0800174 backoffTime = -1; // if < 0 then we aren't in backoff mode
175 backoffDelay = -1; // if < 0 then we aren't in backoff mode
Dianne Hackborn231cc602009-04-27 17:10:36 -0700176 }
177 }
Costin Manolache360e4542009-09-04 13:36:04 -0700178
Dianne Hackborn231cc602009-04-27 17:10:36 -0700179 public static class SyncHistoryItem {
180 int authorityId;
181 int historyId;
182 long eventTime;
183 long elapsedTime;
184 int source;
185 int event;
186 long upstreamActivity;
187 long downstreamActivity;
188 String mesg;
189 }
Costin Manolache360e4542009-09-04 13:36:04 -0700190
Dianne Hackborn231cc602009-04-27 17:10:36 -0700191 public static class DayStats {
192 public final int day;
193 public int successCount;
194 public long successTime;
195 public int failureCount;
196 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700197
Dianne Hackborn231cc602009-04-27 17:10:36 -0700198 public DayStats(int day) {
199 this.day = day;
200 }
201 }
Costin Manolache360e4542009-09-04 13:36:04 -0700202
Dianne Hackborn231cc602009-04-27 17:10:36 -0700203 // Primary list of all syncable authorities. Also our global lock.
204 private final SparseArray<AuthorityInfo> mAuthorities =
205 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700206
Dianne Hackborn7a135592009-05-06 00:28:37 -0700207 private final HashMap<Account, AccountInfo> mAccounts =
208 new HashMap<Account, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209
Dianne Hackborn231cc602009-04-27 17:10:36 -0700210 private final ArrayList<PendingOperation> mPendingOperations =
211 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700212
Dianne Hackborn231cc602009-04-27 17:10:36 -0700213 private ActiveSyncInfo mActiveSync;
Costin Manolache360e4542009-09-04 13:36:04 -0700214
Dianne Hackborn231cc602009-04-27 17:10:36 -0700215 private final SparseArray<SyncStatusInfo> mSyncStatus =
216 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700217
Dianne Hackborn231cc602009-04-27 17:10:36 -0700218 private final ArrayList<SyncHistoryItem> mSyncHistory =
219 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700220
Dianne Hackborn231cc602009-04-27 17:10:36 -0700221 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
222 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700223
Dianne Hackborn231cc602009-04-27 17:10:36 -0700224 // We keep 4 weeks of stats.
225 private final DayStats[] mDayStats = new DayStats[7*4];
226 private final Calendar mCal;
227 private int mYear;
228 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700229
Dianne Hackborn231cc602009-04-27 17:10:36 -0700230 private final Context mContext;
231 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700232
Dianne Hackborn231cc602009-04-27 17:10:36 -0700233 /**
234 * This file contains the core engine state: all accounts and the
235 * settings for them. It must never be lost, and should be changed
236 * infrequently, so it is stored as an XML file.
237 */
238 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700239
Dianne Hackborn231cc602009-04-27 17:10:36 -0700240 /**
241 * This file contains the current sync status. We would like to retain
242 * it across boots, but its loss is not the end of the world, so we store
243 * this information as binary data.
244 */
245 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700246
Dianne Hackborn231cc602009-04-27 17:10:36 -0700247 /**
248 * This file contains sync statistics. This is purely debugging information
249 * so is written infrequently and can be thrown away at any time.
250 */
251 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700252
Dianne Hackborn231cc602009-04-27 17:10:36 -0700253 /**
254 * This file contains the pending sync operations. It is a binary file,
255 * which must be updated every time an operation is added or removed,
256 * so we have special handling of it.
257 */
258 private final AtomicFile mPendingFile;
259 private static final int PENDING_FINISH_TO_WRITE = 4;
260 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700261
Dianne Hackborn231cc602009-04-27 17:10:36 -0700262 private int mNextHistoryId = 0;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700263 private boolean mMasterSyncAutomatically = true;
Costin Manolache360e4542009-09-04 13:36:04 -0700264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 private SyncStorageEngine(Context context) {
266 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700268
Dianne Hackborn231cc602009-04-27 17:10:36 -0700269 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700270
Oscar Montemayora8529f62009-11-18 10:14:20 -0800271 // This call will return the correct directory whether Encrypted File Systems is
272 // enabled or not.
273 File dataDir = Environment.getSecureDataDirectory();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700274 File systemDir = new File(dataDir, "system");
275 File syncDir = new File(systemDir, "sync");
276 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
277 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
278 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
279 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700280
Dianne Hackborn231cc602009-04-27 17:10:36 -0700281 readAccountInfoLocked();
282 readStatusLocked();
283 readPendingOperationsLocked();
284 readStatisticsLocked();
285 readLegacyAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
287
288 public static SyncStorageEngine newTestInstance(Context context) {
289 return new SyncStorageEngine(context);
290 }
291
292 public static void init(Context context) {
293 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800294 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296 sSyncStorageEngine = new SyncStorageEngine(context);
297 }
298
299 public static SyncStorageEngine getSingleton() {
300 if (sSyncStorageEngine == null) {
301 throw new IllegalStateException("not initialized");
302 }
303 return sSyncStorageEngine;
304 }
305
Dianne Hackborn231cc602009-04-27 17:10:36 -0700306 @Override public void handleMessage(Message msg) {
307 if (msg.what == MSG_WRITE_STATUS) {
308 synchronized (mAccounts) {
309 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700310 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700311 } else if (msg.what == MSG_WRITE_STATISTICS) {
312 synchronized (mAccounts) {
313 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315 }
316 }
Costin Manolache360e4542009-09-04 13:36:04 -0700317
Dianne Hackborn231cc602009-04-27 17:10:36 -0700318 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
319 synchronized (mAuthorities) {
320 mChangeListeners.register(callback, mask);
321 }
322 }
Costin Manolache360e4542009-09-04 13:36:04 -0700323
Dianne Hackborn231cc602009-04-27 17:10:36 -0700324 public void removeStatusChangeListener(ISyncStatusObserver callback) {
325 synchronized (mAuthorities) {
326 mChangeListeners.unregister(callback);
327 }
328 }
Costin Manolache360e4542009-09-04 13:36:04 -0700329
Dianne Hackborn231cc602009-04-27 17:10:36 -0700330 private void reportChange(int which) {
331 ArrayList<ISyncStatusObserver> reports = null;
332 synchronized (mAuthorities) {
333 int i = mChangeListeners.beginBroadcast();
334 while (i > 0) {
335 i--;
336 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
337 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 continue;
339 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700340 if (reports == null) {
341 reports = new ArrayList<ISyncStatusObserver>(i);
342 }
343 reports.add(mChangeListeners.getBroadcastItem(i));
344 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700345 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700346 }
Costin Manolache360e4542009-09-04 13:36:04 -0700347
Dianne Hackborn231cc602009-04-27 17:10:36 -0700348 if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
Costin Manolache360e4542009-09-04 13:36:04 -0700349
Dianne Hackborn231cc602009-04-27 17:10:36 -0700350 if (reports != null) {
351 int i = reports.size();
352 while (i > 0) {
353 i--;
354 try {
355 reports.get(i).onStatusChanged(which);
356 } catch (RemoteException e) {
357 // The remote callback list will take care of this for us.
358 }
359 }
360 }
361 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700362
Fred Quintanaac9385e2009-06-22 18:00:59 -0700363 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700364 synchronized (mAuthorities) {
365 if (account != null) {
366 AuthorityInfo authority = getAuthorityLocked(account, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700367 "getSyncAutomatically");
368 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700369 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700370
Dianne Hackborn231cc602009-04-27 17:10:36 -0700371 int i = mAuthorities.size();
372 while (i > 0) {
373 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700374 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700375 if (authority.authority.equals(providerName)
376 && authority.enabled) {
377 return true;
378 }
379 }
380 return false;
381 }
382 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383
Fred Quintanaac9385e2009-06-22 18:00:59 -0700384 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700385 boolean wasEnabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700386 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700387 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
388 wasEnabled = authority.enabled;
389 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700390 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700392
393 if (!wasEnabled && sync) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800394 ContentResolver.requestSync(account, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700395 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700396 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 }
398
Fred Quintana5e787c42009-08-16 23:13:53 -0700399 public int getIsSyncable(Account account, String providerName) {
400 synchronized (mAuthorities) {
401 if (account != null) {
402 AuthorityInfo authority = getAuthorityLocked(account, providerName,
403 "getIsSyncable");
404 if (authority == null) {
405 return -1;
406 }
407 return authority.syncable;
408 }
409
410 int i = mAuthorities.size();
411 while (i > 0) {
412 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700413 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700414 if (authority.authority.equals(providerName)) {
415 return authority.syncable;
416 }
417 }
418 return -1;
419 }
420 }
421
422 public void setIsSyncable(Account account, String providerName, int syncable) {
423 int oldState;
Fred Quintanab763ab22009-08-18 18:07:30 -0700424 if (syncable > 1) {
425 syncable = 1;
426 } else if (syncable < -1) {
427 syncable = -1;
428 }
429 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700430 synchronized (mAuthorities) {
431 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
432 oldState = authority.syncable;
433 authority.syncable = syncable;
434 writeAccountInfoLocked();
435 }
436
437 if (oldState <= 0 && syncable > 0) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800438 ContentResolver.requestSync(account, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700439 }
440 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
441 }
442
Fred Quintana307da1a2010-01-21 14:24:20 -0800443 public Pair<Long, Long> getBackoff(Account account, String providerName) {
444 synchronized (mAuthorities) {
445 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getBackoff");
446 if (authority == null || authority.backoffTime < 0) {
447 return null;
448 }
449 return Pair.create(authority.backoffTime, authority.backoffDelay);
450 }
451 }
452
453 public void setBackoff(Account account, String providerName,
454 long nextSyncTime, long nextDelay) {
455 if (Log.isLoggable(TAG, Log.VERBOSE)) {
456 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
457 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
458 }
459 boolean changed = false;
460 synchronized (mAuthorities) {
461 if (account == null || providerName == null) {
462 for (AccountInfo accountInfo : mAccounts.values()) {
463 if (account != null && !account.equals(accountInfo.account)) continue;
464 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
465 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
466 continue;
467 }
468 if (authorityInfo.backoffTime != nextSyncTime
469 || authorityInfo.backoffDelay != nextDelay) {
470 authorityInfo.backoffTime = nextSyncTime;
471 authorityInfo.backoffDelay = nextDelay;
472 changed = true;
473 }
474 }
475 }
476 } else {
477 AuthorityInfo authority =
478 getOrCreateAuthorityLocked(account, providerName, -1, false);
479 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
480 return;
481 }
482 authority.backoffTime = nextSyncTime;
483 authority.backoffDelay = nextDelay;
484 changed = true;
485 }
486 if (changed) {
487 writeAccountInfoLocked();
488 }
489 }
490
491 if (changed) {
492 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
493 }
494 }
495
496 public void setDelayUntilTime(Account account, String providerName, long delayUntil) {
497 if (Log.isLoggable(TAG, Log.VERBOSE)) {
498 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
499 + " -> delayUntil " + delayUntil);
500 }
501 synchronized (mAuthorities) {
502 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
503 if (authority.delayUntil == delayUntil) {
504 return;
505 }
506 authority.delayUntil = delayUntil;
507 writeAccountInfoLocked();
508 }
509
510 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
511 }
512
513 public long getDelayUntilTime(Account account, String providerName) {
514 synchronized (mAuthorities) {
515 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getDelayUntil");
516 if (authority == null) {
517 return 0;
518 }
519 return authority.delayUntil;
520 }
521 }
522
Fred Quintanaac9385e2009-06-22 18:00:59 -0700523 public void setMasterSyncAutomatically(boolean flag) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700524 boolean old;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700525 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700526 old = mMasterSyncAutomatically;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700527 mMasterSyncAutomatically = flag;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700528 writeAccountInfoLocked();
529 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700530 if (!old && flag) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800531 ContentResolver.requestSync(null, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700532 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700533 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
534 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536
Fred Quintanaac9385e2009-06-22 18:00:59 -0700537 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700538 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700539 return mMasterSyncAutomatically;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700540 }
541 }
Costin Manolache360e4542009-09-04 13:36:04 -0700542
Dianne Hackborn7a135592009-05-06 00:28:37 -0700543 public AuthorityInfo getAuthority(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700544 synchronized (mAuthorities) {
545 return getAuthorityLocked(account, authority, null);
546 }
547 }
Costin Manolache360e4542009-09-04 13:36:04 -0700548
Dianne Hackborn231cc602009-04-27 17:10:36 -0700549 public AuthorityInfo getAuthority(int authorityId) {
550 synchronized (mAuthorities) {
551 return mAuthorities.get(authorityId);
552 }
553 }
Costin Manolache360e4542009-09-04 13:36:04 -0700554
Dianne Hackborn231cc602009-04-27 17:10:36 -0700555 /**
556 * Returns true if there is currently a sync operation for the given
557 * account or authority in the pending list, or actively being processed.
558 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700559 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700560 synchronized (mAuthorities) {
561 int i = mPendingOperations.size();
562 while (i > 0) {
563 i--;
564 // TODO(fredq): this probably shouldn't be considering
565 // pending operations.
566 PendingOperation op = mPendingOperations.get(i);
567 if (op.account.equals(account) && op.authority.equals(authority)) {
568 return true;
569 }
570 }
Costin Manolache360e4542009-09-04 13:36:04 -0700571
Dianne Hackborn231cc602009-04-27 17:10:36 -0700572 if (mActiveSync != null) {
573 AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
574 if (ainfo != null && ainfo.account.equals(account)
575 && ainfo.authority.equals(authority)) {
576 return true;
577 }
578 }
579 }
Costin Manolache360e4542009-09-04 13:36:04 -0700580
Dianne Hackborn231cc602009-04-27 17:10:36 -0700581 return false;
582 }
Costin Manolache360e4542009-09-04 13:36:04 -0700583
Dianne Hackborn231cc602009-04-27 17:10:36 -0700584 public PendingOperation insertIntoPending(PendingOperation op) {
585 synchronized (mAuthorities) {
586 if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
587 + " auth=" + op.authority
588 + " src=" + op.syncSource
589 + " extras=" + op.extras);
Costin Manolache360e4542009-09-04 13:36:04 -0700590
Dianne Hackborn231cc602009-04-27 17:10:36 -0700591 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
592 op.authority,
593 -1 /* desired identifier */,
594 true /* write accounts to storage */);
595 if (authority == null) {
596 return null;
597 }
Costin Manolache360e4542009-09-04 13:36:04 -0700598
Dianne Hackborn231cc602009-04-27 17:10:36 -0700599 op = new PendingOperation(op);
600 op.authorityId = authority.ident;
601 mPendingOperations.add(op);
602 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700603
Dianne Hackborn231cc602009-04-27 17:10:36 -0700604 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
605 status.pending = true;
606 }
Costin Manolache360e4542009-09-04 13:36:04 -0700607
Fred Quintanaac9385e2009-06-22 18:00:59 -0700608 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700609 return op;
610 }
611
612 public boolean deleteFromPending(PendingOperation op) {
613 boolean res = false;
614 synchronized (mAuthorities) {
615 if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
616 + " auth=" + op.authority
617 + " src=" + op.syncSource
618 + " extras=" + op.extras);
619 if (mPendingOperations.remove(op)) {
620 if (mPendingOperations.size() == 0
621 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
622 writePendingOperationsLocked();
623 mNumPendingFinished = 0;
624 } else {
625 mNumPendingFinished++;
626 }
Costin Manolache360e4542009-09-04 13:36:04 -0700627
Dianne Hackborn231cc602009-04-27 17:10:36 -0700628 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
629 "deleteFromPending");
630 if (authority != null) {
631 if (DEBUG) Log.v(TAG, "removing - " + authority);
632 final int N = mPendingOperations.size();
633 boolean morePending = false;
634 for (int i=0; i<N; i++) {
635 PendingOperation cur = mPendingOperations.get(i);
636 if (cur.account.equals(op.account)
637 && cur.authority.equals(op.authority)) {
638 morePending = true;
639 break;
640 }
641 }
Costin Manolache360e4542009-09-04 13:36:04 -0700642
Dianne Hackborn231cc602009-04-27 17:10:36 -0700643 if (!morePending) {
644 if (DEBUG) Log.v(TAG, "no more pending!");
645 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
646 status.pending = false;
647 }
648 }
Costin Manolache360e4542009-09-04 13:36:04 -0700649
Dianne Hackborn231cc602009-04-27 17:10:36 -0700650 res = true;
651 }
652 }
Costin Manolache360e4542009-09-04 13:36:04 -0700653
Fred Quintanaac9385e2009-06-22 18:00:59 -0700654 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700655 return res;
656 }
657
658 public int clearPending() {
659 int num;
660 synchronized (mAuthorities) {
661 if (DEBUG) Log.v(TAG, "clearPending");
662 num = mPendingOperations.size();
663 mPendingOperations.clear();
664 final int N = mSyncStatus.size();
665 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700666 mSyncStatus.valueAt(i).pending = false;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700667 }
668 writePendingOperationsLocked();
669 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700670 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700671 return num;
672 }
673
674 /**
675 * Return a copy of the current array of pending operations. The
676 * PendingOperation objects are the real objects stored inside, so that
677 * they can be used with deleteFromPending().
678 */
679 public ArrayList<PendingOperation> getPendingOperations() {
680 synchronized (mAuthorities) {
681 return new ArrayList<PendingOperation>(mPendingOperations);
682 }
683 }
Costin Manolache360e4542009-09-04 13:36:04 -0700684
Dianne Hackborn231cc602009-04-27 17:10:36 -0700685 /**
686 * Return the number of currently pending operations.
687 */
688 public int getPendingOperationCount() {
689 synchronized (mAuthorities) {
690 return mPendingOperations.size();
691 }
692 }
Costin Manolache360e4542009-09-04 13:36:04 -0700693
Dianne Hackborn231cc602009-04-27 17:10:36 -0700694 /**
695 * Called when the set of account has changed, given the new array of
696 * active accounts.
697 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700698 public void doDatabaseCleanup(Account[] accounts) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700699 synchronized (mAuthorities) {
700 if (DEBUG) Log.w(TAG, "Updating for new accounts...");
701 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
702 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
703 while (accIt.hasNext()) {
704 AccountInfo acc = accIt.next();
705 if (!ArrayUtils.contains(accounts, acc.account)) {
706 // This account no longer exists...
707 if (DEBUG) Log.w(TAG, "Account removed: " + acc.account);
708 for (AuthorityInfo auth : acc.authorities.values()) {
709 removing.put(auth.ident, auth);
710 }
711 accIt.remove();
712 }
713 }
Costin Manolache360e4542009-09-04 13:36:04 -0700714
Dianne Hackborn231cc602009-04-27 17:10:36 -0700715 // Clean out all data structures.
716 int i = removing.size();
717 if (i > 0) {
718 while (i > 0) {
719 i--;
720 int ident = removing.keyAt(i);
721 mAuthorities.remove(ident);
722 int j = mSyncStatus.size();
723 while (j > 0) {
724 j--;
725 if (mSyncStatus.keyAt(j) == ident) {
726 mSyncStatus.remove(mSyncStatus.keyAt(j));
727 }
728 }
729 j = mSyncHistory.size();
730 while (j > 0) {
731 j--;
732 if (mSyncHistory.get(j).authorityId == ident) {
733 mSyncHistory.remove(j);
734 }
735 }
736 }
737 writeAccountInfoLocked();
738 writeStatusLocked();
739 writePendingOperationsLocked();
740 writeStatisticsLocked();
741 }
742 }
743 }
744
745 /**
746 * Called when the currently active sync is changing (there can only be
747 * one at a time). Either supply a valid ActiveSyncContext with information
748 * about the sync, or null to stop the currently active sync.
749 */
750 public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
751 synchronized (mAuthorities) {
752 if (activeSyncContext != null) {
753 if (DEBUG) Log.v(TAG, "setActiveSync: account="
754 + activeSyncContext.mSyncOperation.account
755 + " auth=" + activeSyncContext.mSyncOperation.authority
756 + " src=" + activeSyncContext.mSyncOperation.syncSource
757 + " extras=" + activeSyncContext.mSyncOperation.extras);
758 if (mActiveSync != null) {
759 Log.w(TAG, "setActiveSync called with existing active sync!");
760 }
761 AuthorityInfo authority = getAuthorityLocked(
762 activeSyncContext.mSyncOperation.account,
763 activeSyncContext.mSyncOperation.authority,
764 "setActiveSync");
765 if (authority == null) {
766 return;
767 }
768 mActiveSync = new ActiveSyncInfo(authority.ident,
769 authority.account, authority.authority,
770 activeSyncContext.mStartTime);
771 } else {
772 if (DEBUG) Log.v(TAG, "setActiveSync: null");
773 mActiveSync = null;
774 }
775 }
Costin Manolache360e4542009-09-04 13:36:04 -0700776
Fred Quintanaac9385e2009-06-22 18:00:59 -0700777 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700778 }
779
780 /**
781 * To allow others to send active change reports, to poke clients.
782 */
783 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700784 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700785 }
Costin Manolache360e4542009-09-04 13:36:04 -0700786
Dianne Hackborn231cc602009-04-27 17:10:36 -0700787 /**
788 * Note that sync has started for the given account and authority.
789 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700790 public long insertStartSyncEvent(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700791 long now, int source) {
792 long id;
793 synchronized (mAuthorities) {
794 if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
795 + " auth=" + authorityName + " source=" + source);
796 AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
797 "insertStartSyncEvent");
798 if (authority == null) {
799 return -1;
800 }
801 SyncHistoryItem item = new SyncHistoryItem();
802 item.authorityId = authority.ident;
803 item.historyId = mNextHistoryId++;
804 if (mNextHistoryId < 0) mNextHistoryId = 0;
805 item.eventTime = now;
806 item.source = source;
807 item.event = EVENT_START;
808 mSyncHistory.add(0, item);
809 while (mSyncHistory.size() > MAX_HISTORY) {
810 mSyncHistory.remove(mSyncHistory.size()-1);
811 }
812 id = item.historyId;
813 if (DEBUG) Log.v(TAG, "returning historyId " + id);
814 }
Costin Manolache360e4542009-09-04 13:36:04 -0700815
Fred Quintanaac9385e2009-06-22 18:00:59 -0700816 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700817 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819
820 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
821 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700822 synchronized (mAuthorities) {
823 if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
824 SyncHistoryItem item = null;
825 int i = mSyncHistory.size();
826 while (i > 0) {
827 i--;
828 item = mSyncHistory.get(i);
829 if (item.historyId == historyId) {
830 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700832 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
Costin Manolache360e4542009-09-04 13:36:04 -0700834
Dianne Hackborn231cc602009-04-27 17:10:36 -0700835 if (item == null) {
836 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
837 return;
838 }
Costin Manolache360e4542009-09-04 13:36:04 -0700839
Dianne Hackborn231cc602009-04-27 17:10:36 -0700840 item.elapsedTime = elapsedTime;
841 item.event = EVENT_STOP;
842 item.mesg = resultMessage;
843 item.downstreamActivity = downstreamActivity;
844 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -0700845
Dianne Hackborn231cc602009-04-27 17:10:36 -0700846 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -0700847
Dianne Hackborn231cc602009-04-27 17:10:36 -0700848 status.numSyncs++;
849 status.totalElapsedTime += elapsedTime;
850 switch (item.source) {
851 case SOURCE_LOCAL:
852 status.numSourceLocal++;
853 break;
854 case SOURCE_POLL:
855 status.numSourcePoll++;
856 break;
857 case SOURCE_USER:
858 status.numSourceUser++;
859 break;
860 case SOURCE_SERVER:
861 status.numSourceServer++;
862 break;
863 }
Costin Manolache360e4542009-09-04 13:36:04 -0700864
Dianne Hackborn231cc602009-04-27 17:10:36 -0700865 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700866 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700867 if (mDayStats[0] == null) {
868 mDayStats[0] = new DayStats(day);
869 } else if (day != mDayStats[0].day) {
870 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
871 mDayStats[0] = new DayStats(day);
872 writeStatisticsNow = true;
873 } else if (mDayStats[0] == null) {
874 }
875 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -0700876
Dianne Hackborn231cc602009-04-27 17:10:36 -0700877 final long lastSyncTime = (item.eventTime + elapsedTime);
878 boolean writeStatusNow = false;
879 if (MESG_SUCCESS.equals(resultMessage)) {
880 // - if successful, update the successful columns
881 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
882 writeStatusNow = true;
883 }
884 status.lastSuccessTime = lastSyncTime;
885 status.lastSuccessSource = item.source;
886 status.lastFailureTime = 0;
887 status.lastFailureSource = -1;
888 status.lastFailureMesg = null;
889 status.initialFailureTime = 0;
890 ds.successCount++;
891 ds.successTime += elapsedTime;
892 } else if (!MESG_CANCELED.equals(resultMessage)) {
893 if (status.lastFailureTime == 0) {
894 writeStatusNow = true;
895 }
896 status.lastFailureTime = lastSyncTime;
897 status.lastFailureSource = item.source;
898 status.lastFailureMesg = resultMessage;
899 if (status.initialFailureTime == 0) {
900 status.initialFailureTime = lastSyncTime;
901 }
902 ds.failureCount++;
903 ds.failureTime += elapsedTime;
904 }
Costin Manolache360e4542009-09-04 13:36:04 -0700905
Dianne Hackborn231cc602009-04-27 17:10:36 -0700906 if (writeStatusNow) {
907 writeStatusLocked();
908 } else if (!hasMessages(MSG_WRITE_STATUS)) {
909 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
910 WRITE_STATUS_DELAY);
911 }
912 if (writeStatisticsNow) {
913 writeStatisticsLocked();
914 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
915 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
916 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -0700917 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700918 }
Costin Manolache360e4542009-09-04 13:36:04 -0700919
Fred Quintanaac9385e2009-06-22 18:00:59 -0700920 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700921 }
922
923 /**
924 * Return the currently active sync information, or null if there is no
925 * active sync. Note that the returned object is the real, live active
926 * sync object, so be careful what you do with it.
927 */
928 public ActiveSyncInfo getActiveSync() {
929 synchronized (mAuthorities) {
930 return mActiveSync;
931 }
932 }
Costin Manolache360e4542009-09-04 13:36:04 -0700933
Dianne Hackborn231cc602009-04-27 17:10:36 -0700934 /**
935 * Return an array of the current sync status for all authorities. Note
936 * that the objects inside the array are the real, live status objects,
937 * so be careful what you do with them.
938 */
939 public ArrayList<SyncStatusInfo> getSyncStatus() {
940 synchronized (mAuthorities) {
941 final int N = mSyncStatus.size();
942 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
943 for (int i=0; i<N; i++) {
944 ops.add(mSyncStatus.valueAt(i));
945 }
946 return ops;
947 }
948 }
Costin Manolache360e4542009-09-04 13:36:04 -0700949
Dianne Hackborn231cc602009-04-27 17:10:36 -0700950 /**
Costin Manolacheb7520982009-09-02 18:03:05 -0700951 * Returns the status that matches the authority and account.
952 *
953 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -0700954 * @param authority the authority whose row should be selected
955 * @return the SyncStatusInfo for the authority, or null if none exists
956 */
Costin Manolacheb7520982009-09-02 18:03:05 -0700957 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
958 if (account == null || authority == null) {
959 throw new IllegalArgumentException();
960 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700961 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700962 final int N = mSyncStatus.size();
963 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700964 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700965 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -0700966
967 if (ainfo != null && ainfo.authority.equals(authority) &&
968 account.equals(ainfo.account)) {
969 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700970 }
971 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700972 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700973 }
974 }
Costin Manolache360e4542009-09-04 13:36:04 -0700975
Dianne Hackborn231cc602009-04-27 17:10:36 -0700976 /**
977 * Return true if the pending status is true of any matching authorities.
978 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700979 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700980 synchronized (mAuthorities) {
981 final int N = mSyncStatus.size();
982 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700983 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700984 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
985 if (ainfo == null) {
986 continue;
987 }
988 if (account != null && !ainfo.account.equals(account)) {
989 continue;
990 }
991 if (ainfo.authority.equals(authority) && cur.pending) {
992 return true;
993 }
994 }
995 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997 }
998
999 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001000 * Return an array of the current sync status for all authorities. Note
1001 * that the objects inside the array are the real, live status objects,
1002 * so be careful what you do with them.
1003 */
1004 public ArrayList<SyncHistoryItem> getSyncHistory() {
1005 synchronized (mAuthorities) {
1006 final int N = mSyncHistory.size();
1007 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1008 for (int i=0; i<N; i++) {
1009 items.add(mSyncHistory.get(i));
1010 }
1011 return items;
1012 }
1013 }
Costin Manolache360e4542009-09-04 13:36:04 -07001014
Dianne Hackborn231cc602009-04-27 17:10:36 -07001015 /**
1016 * Return an array of the current per-day statistics. Note
1017 * that the objects inside the array are the real, live status objects,
1018 * so be careful what you do with them.
1019 */
1020 public DayStats[] getDayStatistics() {
1021 synchronized (mAuthorities) {
1022 DayStats[] ds = new DayStats[mDayStats.length];
1023 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1024 return ds;
1025 }
1026 }
Costin Manolache360e4542009-09-04 13:36:04 -07001027
Dianne Hackborn231cc602009-04-27 17:10:36 -07001028 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 * If sync is failing for any of the provider/accounts then determine the time at which it
1030 * started failing and return the earliest time over all the provider/accounts. If none are
1031 * failing then return 0.
1032 */
1033 public long getInitialSyncFailureTime() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001034 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001035 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001036 return 0;
1037 }
Costin Manolache360e4542009-09-04 13:36:04 -07001038
Dianne Hackborn231cc602009-04-27 17:10:36 -07001039 long oldest = 0;
1040 int i = mSyncStatus.size();
1041 while (i > 0) {
1042 i--;
1043 SyncStatusInfo stats = mSyncStatus.valueAt(i);
1044 AuthorityInfo authority = mAuthorities.get(stats.authorityId);
1045 if (authority != null && authority.enabled) {
1046 if (oldest == 0 || stats.initialFailureTime < oldest) {
1047 oldest = stats.initialFailureTime;
1048 }
1049 }
1050 }
Costin Manolache360e4542009-09-04 13:36:04 -07001051
Dianne Hackborn231cc602009-04-27 17:10:36 -07001052 return oldest;
1053 }
1054 }
Costin Manolache360e4542009-09-04 13:36:04 -07001055
Dianne Hackborn55280a92009-05-07 15:53:46 -07001056 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001057 mCal.setTimeInMillis(System.currentTimeMillis());
1058 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1059 if (mYear != mCal.get(Calendar.YEAR)) {
1060 mYear = mCal.get(Calendar.YEAR);
1061 mCal.clear();
1062 mCal.set(Calendar.YEAR, mYear);
1063 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1064 }
1065 return dayOfYear + mYearInDays;
1066 }
Costin Manolache360e4542009-09-04 13:36:04 -07001067
Dianne Hackborn231cc602009-04-27 17:10:36 -07001068 /**
1069 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001070 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001071 * @param accountName The name of the account for the authority.
1072 * @param authorityName The name of the authority itself.
1073 * @param tag If non-null, this will be used in a log message if the
1074 * requested authority does not exist.
1075 */
Dianne Hackborn7a135592009-05-06 00:28:37 -07001076 private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001077 String tag) {
1078 AccountInfo account = mAccounts.get(accountName);
1079 if (account == null) {
1080 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001081 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1082 Log.v(TAG, tag + ": unknown account " + accountName);
1083 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001084 }
1085 return null;
1086 }
1087 AuthorityInfo authority = account.authorities.get(authorityName);
1088 if (authority == null) {
1089 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001090 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1091 Log.v(TAG, tag + ": unknown authority " + authorityName);
1092 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001093 }
1094 return null;
1095 }
Costin Manolache360e4542009-09-04 13:36:04 -07001096
Dianne Hackborn231cc602009-04-27 17:10:36 -07001097 return authority;
1098 }
Costin Manolache360e4542009-09-04 13:36:04 -07001099
Dianne Hackborn7a135592009-05-06 00:28:37 -07001100 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001101 String authorityName, int ident, boolean doWrite) {
1102 AccountInfo account = mAccounts.get(accountName);
1103 if (account == null) {
1104 account = new AccountInfo(accountName);
1105 mAccounts.put(accountName, account);
1106 }
1107 AuthorityInfo authority = account.authorities.get(authorityName);
1108 if (authority == null) {
1109 if (ident < 0) {
1110 // Look for a new identifier for this authority.
1111 final int N = mAuthorities.size();
1112 ident = 0;
1113 for (int i=0; i<N; i++) {
1114 if (mAuthorities.valueAt(i).ident > ident) {
1115 break;
1116 }
1117 ident++;
1118 }
1119 }
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -07001120 if (DEBUG) Log.v(TAG, "created a new AuthorityInfo for " + accountName
Fred Quintanab763ab22009-08-18 18:07:30 -07001121 + ", provider " + authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001122 authority = new AuthorityInfo(accountName, authorityName, ident);
1123 account.authorities.put(authorityName, authority);
1124 mAuthorities.put(ident, authority);
1125 if (doWrite) {
1126 writeAccountInfoLocked();
1127 }
1128 }
Costin Manolache360e4542009-09-04 13:36:04 -07001129
Dianne Hackborn231cc602009-04-27 17:10:36 -07001130 return authority;
1131 }
Costin Manolache360e4542009-09-04 13:36:04 -07001132
Dianne Hackborn231cc602009-04-27 17:10:36 -07001133 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1134 SyncStatusInfo status = mSyncStatus.get(authorityId);
1135 if (status == null) {
1136 status = new SyncStatusInfo(authorityId);
1137 mSyncStatus.put(authorityId, status);
1138 }
1139 return status;
1140 }
Costin Manolache360e4542009-09-04 13:36:04 -07001141
Dianne Hackborn55280a92009-05-07 15:53:46 -07001142 public void writeAllState() {
1143 synchronized (mAuthorities) {
1144 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001145
Dianne Hackborn55280a92009-05-07 15:53:46 -07001146 if (mNumPendingFinished > 0) {
1147 // Only write these if they are out of date.
1148 writePendingOperationsLocked();
1149 }
Costin Manolache360e4542009-09-04 13:36:04 -07001150
Dianne Hackborn55280a92009-05-07 15:53:46 -07001151 // Just always write these... they are likely out of date.
1152 writeStatusLocked();
1153 writeStatisticsLocked();
1154 }
1155 }
Costin Manolache360e4542009-09-04 13:36:04 -07001156
Dianne Hackborn231cc602009-04-27 17:10:36 -07001157 /**
1158 * Read all account information back in to the initial engine state.
1159 */
1160 private void readAccountInfoLocked() {
1161 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001163 fis = mAccountInfoFile.openRead();
1164 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1165 XmlPullParser parser = Xml.newPullParser();
1166 parser.setInput(fis, null);
1167 int eventType = parser.getEventType();
1168 while (eventType != XmlPullParser.START_TAG) {
1169 eventType = parser.next();
1170 }
1171 String tagName = parser.getName();
1172 if ("accounts".equals(tagName)) {
1173 String listen = parser.getAttributeValue(
1174 null, "listen-for-tickles");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001175 mMasterSyncAutomatically = listen == null
Dianne Hackborn231cc602009-04-27 17:10:36 -07001176 || Boolean.parseBoolean(listen);
1177 eventType = parser.next();
1178 do {
1179 if (eventType == XmlPullParser.START_TAG
1180 && parser.getDepth() == 2) {
1181 tagName = parser.getName();
1182 if ("authority".equals(tagName)) {
1183 int id = -1;
1184 try {
1185 id = Integer.parseInt(parser.getAttributeValue(
1186 null, "id"));
1187 } catch (NumberFormatException e) {
1188 } catch (NullPointerException e) {
1189 }
1190 if (id >= 0) {
1191 String accountName = parser.getAttributeValue(
1192 null, "account");
Dianne Hackborn7a135592009-05-06 00:28:37 -07001193 String accountType = parser.getAttributeValue(
1194 null, "type");
1195 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001196 accountType = "com.google";
Dianne Hackborn7a135592009-05-06 00:28:37 -07001197 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001198 String authorityName = parser.getAttributeValue(
1199 null, "authority");
1200 String enabled = parser.getAttributeValue(
1201 null, "enabled");
Fred Quintana5e787c42009-08-16 23:13:53 -07001202 String syncable = parser.getAttributeValue(null, "syncable");
1203 AuthorityInfo authority = mAuthorities.get(id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001204 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1205 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001206 + " enabled=" + enabled
1207 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001208 if (authority == null) {
1209 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1210 authority = getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001211 new Account(accountName, accountType),
1212 authorityName, id, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001213 }
1214 if (authority != null) {
1215 authority.enabled = enabled == null
1216 || Boolean.parseBoolean(enabled);
Fred Quintana5e787c42009-08-16 23:13:53 -07001217 if ("unknown".equals(syncable)) {
1218 authority.syncable = -1;
1219 } else {
1220 authority.syncable =
1221 (syncable == null || Boolean.parseBoolean(enabled))
1222 ? 1
1223 : 0;
1224 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001225 } else {
1226 Log.w(TAG, "Failure adding authority: account="
1227 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001228 + " enabled=" + enabled
1229 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001230 }
1231 }
1232 }
1233 }
1234 eventType = parser.next();
1235 } while (eventType != XmlPullParser.END_DOCUMENT);
1236 }
1237 } catch (XmlPullParserException e) {
1238 Log.w(TAG, "Error reading accounts", e);
1239 } catch (java.io.IOException e) {
1240 if (fis == null) Log.i(TAG, "No initial accounts");
1241 else Log.w(TAG, "Error reading accounts", e);
1242 } finally {
1243 if (fis != null) {
1244 try {
1245 fis.close();
1246 } catch (java.io.IOException e1) {
1247 }
1248 }
1249 }
1250 }
Costin Manolache360e4542009-09-04 13:36:04 -07001251
Dianne Hackborn231cc602009-04-27 17:10:36 -07001252 /**
1253 * Write all account information to the account file.
1254 */
1255 private void writeAccountInfoLocked() {
1256 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1257 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001258
Dianne Hackborn231cc602009-04-27 17:10:36 -07001259 try {
1260 fos = mAccountInfoFile.startWrite();
1261 XmlSerializer out = new FastXmlSerializer();
1262 out.setOutput(fos, "utf-8");
1263 out.startDocument(null, true);
1264 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001265
Dianne Hackborn231cc602009-04-27 17:10:36 -07001266 out.startTag(null, "accounts");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001267 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001268 out.attribute(null, "listen-for-tickles", "false");
1269 }
Costin Manolache360e4542009-09-04 13:36:04 -07001270
Dianne Hackborn231cc602009-04-27 17:10:36 -07001271 final int N = mAuthorities.size();
1272 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001273 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001274 out.startTag(null, "authority");
1275 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001276 out.attribute(null, "account", authority.account.name);
1277 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001278 out.attribute(null, "authority", authority.authority);
1279 if (!authority.enabled) {
1280 out.attribute(null, "enabled", "false");
1281 }
Fred Quintana5e787c42009-08-16 23:13:53 -07001282 if (authority.syncable < 0) {
1283 out.attribute(null, "syncable", "unknown");
1284 } else if (authority.syncable == 0) {
1285 out.attribute(null, "syncable", "false");
1286 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001287 out.endTag(null, "authority");
1288 }
Costin Manolache360e4542009-09-04 13:36:04 -07001289
Dianne Hackborn231cc602009-04-27 17:10:36 -07001290 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001291
Dianne Hackborn231cc602009-04-27 17:10:36 -07001292 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001293
Dianne Hackborn231cc602009-04-27 17:10:36 -07001294 mAccountInfoFile.finishWrite(fos);
1295 } catch (java.io.IOException e1) {
1296 Log.w(TAG, "Error writing accounts", e1);
1297 if (fos != null) {
1298 mAccountInfoFile.failWrite(fos);
1299 }
1300 }
1301 }
Costin Manolache360e4542009-09-04 13:36:04 -07001302
Dianne Hackborn231cc602009-04-27 17:10:36 -07001303 static int getIntColumn(Cursor c, String name) {
1304 return c.getInt(c.getColumnIndex(name));
1305 }
Costin Manolache360e4542009-09-04 13:36:04 -07001306
Dianne Hackborn231cc602009-04-27 17:10:36 -07001307 static long getLongColumn(Cursor c, String name) {
1308 return c.getLong(c.getColumnIndex(name));
1309 }
Costin Manolache360e4542009-09-04 13:36:04 -07001310
Dianne Hackborn231cc602009-04-27 17:10:36 -07001311 /**
1312 * Load sync engine state from the old syncmanager database, and then
1313 * erase it. Note that we don't deal with pending operations, active
1314 * sync, or history.
1315 */
1316 private void readLegacyAccountInfoLocked() {
1317 // Look for old database to initialize from.
1318 File file = mContext.getDatabasePath("syncmanager.db");
1319 if (!file.exists()) {
1320 return;
1321 }
1322 String path = file.getPath();
1323 SQLiteDatabase db = null;
1324 try {
1325 db = SQLiteDatabase.openDatabase(path, null,
1326 SQLiteDatabase.OPEN_READONLY);
1327 } catch (SQLiteException e) {
1328 }
Costin Manolache360e4542009-09-04 13:36:04 -07001329
Dianne Hackborn231cc602009-04-27 17:10:36 -07001330 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001331 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001332
Dianne Hackborn231cc602009-04-27 17:10:36 -07001333 // Copy in all of the status information, as well as accounts.
1334 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1335 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1336 qb.setTables("stats, status");
1337 HashMap<String,String> map = new HashMap<String,String>();
1338 map.put("_id", "status._id as _id");
1339 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001340 if (hasType) {
1341 map.put("account_type", "stats.account_type as account_type");
1342 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001343 map.put("authority", "stats.authority as authority");
1344 map.put("totalElapsedTime", "totalElapsedTime");
1345 map.put("numSyncs", "numSyncs");
1346 map.put("numSourceLocal", "numSourceLocal");
1347 map.put("numSourcePoll", "numSourcePoll");
1348 map.put("numSourceServer", "numSourceServer");
1349 map.put("numSourceUser", "numSourceUser");
1350 map.put("lastSuccessSource", "lastSuccessSource");
1351 map.put("lastSuccessTime", "lastSuccessTime");
1352 map.put("lastFailureSource", "lastFailureSource");
1353 map.put("lastFailureTime", "lastFailureTime");
1354 map.put("lastFailureMesg", "lastFailureMesg");
1355 map.put("pending", "pending");
1356 qb.setProjectionMap(map);
1357 qb.appendWhere("stats._id = status.stats_id");
1358 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001360 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001361 String accountType = hasType
1362 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001363 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001364 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001366 String authorityName = c.getString(c.getColumnIndex("authority"));
1367 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001368 new Account(accountName, accountType),
1369 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001370 if (authority != null) {
1371 int i = mSyncStatus.size();
1372 boolean found = false;
1373 SyncStatusInfo st = null;
1374 while (i > 0) {
1375 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001376 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001377 if (st.authorityId == authority.ident) {
1378 found = true;
1379 break;
1380 }
1381 }
1382 if (!found) {
1383 st = new SyncStatusInfo(authority.ident);
1384 mSyncStatus.put(authority.ident, st);
1385 }
1386 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1387 st.numSyncs = getIntColumn(c, "numSyncs");
1388 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1389 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1390 st.numSourceServer = getIntColumn(c, "numSourceServer");
1391 st.numSourceUser = getIntColumn(c, "numSourceUser");
1392 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1393 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1394 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1395 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1396 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1397 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 }
Costin Manolache360e4542009-09-04 13:36:04 -07001400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001402
Dianne Hackborn231cc602009-04-27 17:10:36 -07001403 // Retrieve the settings.
1404 qb = new SQLiteQueryBuilder();
1405 qb.setTables("settings");
1406 c = qb.query(db, null, null, null, null, null, null);
1407 while (c.moveToNext()) {
1408 String name = c.getString(c.getColumnIndex("name"));
1409 String value = c.getString(c.getColumnIndex("value"));
1410 if (name == null) continue;
1411 if (name.equals("listen_for_tickles")) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001412 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001413 } else if (name.startsWith("sync_provider_")) {
1414 String provider = name.substring("sync_provider_".length(),
1415 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001416 int i = mAuthorities.size();
1417 while (i > 0) {
1418 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001419 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001420 if (authority.authority.equals(provider)) {
1421 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001422 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001423 }
1424 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 }
Costin Manolache360e4542009-09-04 13:36:04 -07001427
Dianne Hackborn231cc602009-04-27 17:10:36 -07001428 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001429
Dianne Hackborn231cc602009-04-27 17:10:36 -07001430 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001431
Dianne Hackborn231cc602009-04-27 17:10:36 -07001432 writeAccountInfoLocked();
1433 writeStatusLocked();
1434 (new File(path)).delete();
1435 }
1436 }
Costin Manolache360e4542009-09-04 13:36:04 -07001437
Dianne Hackborn231cc602009-04-27 17:10:36 -07001438 public static final int STATUS_FILE_END = 0;
1439 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001440
Dianne Hackborn231cc602009-04-27 17:10:36 -07001441 /**
1442 * Read all sync status back in to the initial engine state.
1443 */
1444 private void readStatusLocked() {
1445 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1446 try {
1447 byte[] data = mStatusFile.readFully();
1448 Parcel in = Parcel.obtain();
1449 in.unmarshall(data, 0, data.length);
1450 in.setDataPosition(0);
1451 int token;
1452 while ((token=in.readInt()) != STATUS_FILE_END) {
1453 if (token == STATUS_FILE_ITEM) {
1454 SyncStatusInfo status = new SyncStatusInfo(in);
1455 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1456 status.pending = false;
1457 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1458 + status.authorityId);
1459 mSyncStatus.put(status.authorityId, status);
1460 }
1461 } else {
1462 // Ooops.
1463 Log.w(TAG, "Unknown status token: " + token);
1464 break;
1465 }
1466 }
1467 } catch (java.io.IOException e) {
1468 Log.i(TAG, "No initial status");
1469 }
1470 }
Costin Manolache360e4542009-09-04 13:36:04 -07001471
Dianne Hackborn231cc602009-04-27 17:10:36 -07001472 /**
1473 * Write all sync status to the sync status file.
1474 */
1475 private void writeStatusLocked() {
1476 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001477
Dianne Hackborn231cc602009-04-27 17:10:36 -07001478 // The file is being written, so we don't need to have a scheduled
1479 // write until the next change.
1480 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001481
Dianne Hackborn231cc602009-04-27 17:10:36 -07001482 FileOutputStream fos = null;
1483 try {
1484 fos = mStatusFile.startWrite();
1485 Parcel out = Parcel.obtain();
1486 final int N = mSyncStatus.size();
1487 for (int i=0; i<N; i++) {
1488 SyncStatusInfo status = mSyncStatus.valueAt(i);
1489 out.writeInt(STATUS_FILE_ITEM);
1490 status.writeToParcel(out, 0);
1491 }
1492 out.writeInt(STATUS_FILE_END);
1493 fos.write(out.marshall());
1494 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001495
Dianne Hackborn231cc602009-04-27 17:10:36 -07001496 mStatusFile.finishWrite(fos);
1497 } catch (java.io.IOException e1) {
1498 Log.w(TAG, "Error writing status", e1);
1499 if (fos != null) {
1500 mStatusFile.failWrite(fos);
1501 }
1502 }
1503 }
Costin Manolache360e4542009-09-04 13:36:04 -07001504
Fred Quintana307da1a2010-01-21 14:24:20 -08001505 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07001506
Dianne Hackborn231cc602009-04-27 17:10:36 -07001507 /**
1508 * Read all pending operations back in to the initial engine state.
1509 */
1510 private void readPendingOperationsLocked() {
1511 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1512 try {
1513 byte[] data = mPendingFile.readFully();
1514 Parcel in = Parcel.obtain();
1515 in.unmarshall(data, 0, data.length);
1516 in.setDataPosition(0);
1517 final int SIZE = in.dataSize();
1518 while (in.dataPosition() < SIZE) {
1519 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08001520 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001521 Log.w(TAG, "Unknown pending operation version "
1522 + version + "; dropping all ops");
1523 break;
1524 }
1525 int authorityId = in.readInt();
1526 int syncSource = in.readInt();
1527 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08001528 boolean expedited;
1529 if (version == PENDING_OPERATION_VERSION) {
1530 expedited = in.readInt() != 0;
1531 } else {
1532 expedited = false;
1533 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001534 AuthorityInfo authority = mAuthorities.get(authorityId);
1535 if (authority != null) {
1536 Bundle extras = null;
1537 if (flatExtras != null) {
1538 extras = unflattenBundle(flatExtras);
1539 }
1540 PendingOperation op = new PendingOperation(
1541 authority.account, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08001542 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001543 op.authorityId = authorityId;
1544 op.flatExtras = flatExtras;
1545 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
1546 + " auth=" + op.authority
1547 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08001548 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07001549 + " extras=" + op.extras);
1550 mPendingOperations.add(op);
1551 }
1552 }
1553 } catch (java.io.IOException e) {
1554 Log.i(TAG, "No initial pending operations");
1555 }
1556 }
Costin Manolache360e4542009-09-04 13:36:04 -07001557
Dianne Hackborn231cc602009-04-27 17:10:36 -07001558 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
1559 out.writeInt(PENDING_OPERATION_VERSION);
1560 out.writeInt(op.authorityId);
1561 out.writeInt(op.syncSource);
1562 if (op.flatExtras == null && op.extras != null) {
1563 op.flatExtras = flattenBundle(op.extras);
1564 }
1565 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08001566 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001567 }
Costin Manolache360e4542009-09-04 13:36:04 -07001568
Dianne Hackborn231cc602009-04-27 17:10:36 -07001569 /**
1570 * Write all currently pending ops to the pending ops file.
1571 */
1572 private void writePendingOperationsLocked() {
1573 final int N = mPendingOperations.size();
1574 FileOutputStream fos = null;
1575 try {
1576 if (N == 0) {
1577 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
1578 mPendingFile.truncate();
1579 return;
1580 }
Costin Manolache360e4542009-09-04 13:36:04 -07001581
Dianne Hackborn231cc602009-04-27 17:10:36 -07001582 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
1583 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07001584
Dianne Hackborn231cc602009-04-27 17:10:36 -07001585 Parcel out = Parcel.obtain();
1586 for (int i=0; i<N; i++) {
1587 PendingOperation op = mPendingOperations.get(i);
1588 writePendingOperationLocked(op, out);
1589 }
1590 fos.write(out.marshall());
1591 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001592
Dianne Hackborn231cc602009-04-27 17:10:36 -07001593 mPendingFile.finishWrite(fos);
1594 } catch (java.io.IOException e1) {
1595 Log.w(TAG, "Error writing pending operations", e1);
1596 if (fos != null) {
1597 mPendingFile.failWrite(fos);
1598 }
1599 }
1600 }
Costin Manolache360e4542009-09-04 13:36:04 -07001601
Dianne Hackborn231cc602009-04-27 17:10:36 -07001602 /**
1603 * Append the given operation to the pending ops file; if unable to,
1604 * write all pending ops.
1605 */
1606 private void appendPendingOperationLocked(PendingOperation op) {
1607 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
1608 FileOutputStream fos = null;
1609 try {
1610 fos = mPendingFile.openAppend();
1611 } catch (java.io.IOException e) {
1612 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
1613 writePendingOperationsLocked();
1614 return;
1615 }
Costin Manolache360e4542009-09-04 13:36:04 -07001616
Dianne Hackborn231cc602009-04-27 17:10:36 -07001617 try {
1618 Parcel out = Parcel.obtain();
1619 writePendingOperationLocked(op, out);
1620 fos.write(out.marshall());
1621 out.recycle();
1622 } catch (java.io.IOException e1) {
1623 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001625 try {
1626 fos.close();
1627 } catch (java.io.IOException e2) {
1628 }
1629 }
1630 }
Costin Manolache360e4542009-09-04 13:36:04 -07001631
Dianne Hackborn231cc602009-04-27 17:10:36 -07001632 static private byte[] flattenBundle(Bundle bundle) {
1633 byte[] flatData = null;
1634 Parcel parcel = Parcel.obtain();
1635 try {
1636 bundle.writeToParcel(parcel, 0);
1637 flatData = parcel.marshall();
1638 } finally {
1639 parcel.recycle();
1640 }
1641 return flatData;
1642 }
Costin Manolache360e4542009-09-04 13:36:04 -07001643
Dianne Hackborn231cc602009-04-27 17:10:36 -07001644 static private Bundle unflattenBundle(byte[] flatData) {
1645 Bundle bundle;
1646 Parcel parcel = Parcel.obtain();
1647 try {
1648 parcel.unmarshall(flatData, 0, flatData.length);
1649 parcel.setDataPosition(0);
1650 bundle = parcel.readBundle();
1651 } catch (RuntimeException e) {
1652 // A RuntimeException is thrown if we were unable to parse the parcel.
1653 // Create an empty parcel in this case.
1654 bundle = new Bundle();
1655 } finally {
1656 parcel.recycle();
1657 }
1658 return bundle;
1659 }
Costin Manolache360e4542009-09-04 13:36:04 -07001660
Dianne Hackborn231cc602009-04-27 17:10:36 -07001661 public static final int STATISTICS_FILE_END = 0;
1662 public static final int STATISTICS_FILE_ITEM_OLD = 100;
1663 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07001664
Dianne Hackborn231cc602009-04-27 17:10:36 -07001665 /**
1666 * Read all sync statistics back in to the initial engine state.
1667 */
1668 private void readStatisticsLocked() {
1669 try {
1670 byte[] data = mStatisticsFile.readFully();
1671 Parcel in = Parcel.obtain();
1672 in.unmarshall(data, 0, data.length);
1673 in.setDataPosition(0);
1674 int token;
1675 int index = 0;
1676 while ((token=in.readInt()) != STATISTICS_FILE_END) {
1677 if (token == STATISTICS_FILE_ITEM
1678 || token == STATISTICS_FILE_ITEM_OLD) {
1679 int day = in.readInt();
1680 if (token == STATISTICS_FILE_ITEM_OLD) {
1681 day = day - 2009 + 14245; // Magic!
1682 }
1683 DayStats ds = new DayStats(day);
1684 ds.successCount = in.readInt();
1685 ds.successTime = in.readLong();
1686 ds.failureCount = in.readInt();
1687 ds.failureTime = in.readLong();
1688 if (index < mDayStats.length) {
1689 mDayStats[index] = ds;
1690 index++;
1691 }
1692 } else {
1693 // Ooops.
1694 Log.w(TAG, "Unknown stats token: " + token);
1695 break;
1696 }
1697 }
1698 } catch (java.io.IOException e) {
1699 Log.i(TAG, "No initial statistics");
1700 }
1701 }
Costin Manolache360e4542009-09-04 13:36:04 -07001702
Dianne Hackborn231cc602009-04-27 17:10:36 -07001703 /**
1704 * Write all sync statistics to the sync status file.
1705 */
1706 private void writeStatisticsLocked() {
1707 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001708
Dianne Hackborn231cc602009-04-27 17:10:36 -07001709 // The file is being written, so we don't need to have a scheduled
1710 // write until the next change.
1711 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07001712
Dianne Hackborn231cc602009-04-27 17:10:36 -07001713 FileOutputStream fos = null;
1714 try {
1715 fos = mStatisticsFile.startWrite();
1716 Parcel out = Parcel.obtain();
1717 final int N = mDayStats.length;
1718 for (int i=0; i<N; i++) {
1719 DayStats ds = mDayStats[i];
1720 if (ds == null) {
1721 break;
1722 }
1723 out.writeInt(STATISTICS_FILE_ITEM);
1724 out.writeInt(ds.day);
1725 out.writeInt(ds.successCount);
1726 out.writeLong(ds.successTime);
1727 out.writeInt(ds.failureCount);
1728 out.writeLong(ds.failureTime);
1729 }
1730 out.writeInt(STATISTICS_FILE_END);
1731 fos.write(out.marshall());
1732 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001733
Dianne Hackborn231cc602009-04-27 17:10:36 -07001734 mStatisticsFile.finishWrite(fos);
1735 } catch (java.io.IOException e1) {
1736 Log.w(TAG, "Error writing stats", e1);
1737 if (fos != null) {
1738 mStatisticsFile.failWrite(fos);
1739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 }
1741 }
1742}