blob: 07a1f46aa91b10f3a27bb0509912cefd9d5b99d3 [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;
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 = false;
63 private static final boolean DEBUG_FILE = false;
Costin Manolache360e4542009-09-04 13:36:04 -070064
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080065 private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
66
Dianne Hackborn231cc602009-04-27 17:10:36 -070067 // @VisibleForTesting
68 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
Dianne Hackborn231cc602009-04-27 17:10:36 -070070 /** Enum value for a sync start event. */
71 public static final int EVENT_START = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
Dianne Hackborn231cc602009-04-27 17:10:36 -070073 /** Enum value for a sync stop event. */
74 public static final int EVENT_STOP = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
Dianne Hackborn231cc602009-04-27 17:10:36 -070076 // TODO: i18n -- grab these out of resources.
77 /** String names for the sync event types. */
78 public static final String[] EVENTS = { "START", "STOP" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Dianne Hackborn231cc602009-04-27 17:10:36 -070080 /** Enum value for a server-initiated sync. */
81 public static final int SOURCE_SERVER = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Dianne Hackborn231cc602009-04-27 17:10:36 -070083 /** Enum value for a local-initiated sync. */
84 public static final int SOURCE_LOCAL = 1;
85 /**
86 * Enum value for a poll-based sync (e.g., upon connection to
87 * network)
88 */
89 public static final int SOURCE_POLL = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
Dianne Hackborn231cc602009-04-27 17:10:36 -070091 /** Enum value for a user-initiated sync. */
92 public static final int SOURCE_USER = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080094 /** Enum value for a periodic sync. */
95 public static final int SOURCE_PERIODIC = 4;
96
Fred Quintana307da1a2010-01-21 14:24:20 -080097 public static final long NOT_IN_BACKOFF_MODE = -1;
98
Fred Quintanaac9385e2009-06-22 18:00:59 -070099 private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
100 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
101
Dianne Hackborn231cc602009-04-27 17:10:36 -0700102 // TODO: i18n -- grab these out of resources.
103 /** String names for the sync source types. */
104 public static final String[] SOURCES = { "SERVER",
105 "LOCAL",
106 "POLL",
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800107 "USER",
108 "PERIODIC" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
Dianne Hackborn231cc602009-04-27 17:10:36 -0700110 // The MESG column will contain one of these or one of the Error types.
111 public static final String MESG_SUCCESS = "success";
112 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700114 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700115
Dianne Hackborn231cc602009-04-27 17:10:36 -0700116 private static final int MSG_WRITE_STATUS = 1;
117 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700118
Dianne Hackborn231cc602009-04-27 17:10:36 -0700119 private static final int MSG_WRITE_STATISTICS = 2;
120 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700121
122 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700123
Dianne Hackborn231cc602009-04-27 17:10:36 -0700124 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700125 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700126 final int syncSource;
127 final String authority;
128 final Bundle extras; // note: read-only.
Fred Quintana307da1a2010-01-21 14:24:20 -0800129 final boolean expedited;
Costin Manolache360e4542009-09-04 13:36:04 -0700130
Dianne Hackborn231cc602009-04-27 17:10:36 -0700131 int authorityId;
132 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700133
Dianne Hackborn7a135592009-05-06 00:28:37 -0700134 PendingOperation(Account account, int source,
Fred Quintana307da1a2010-01-21 14:24:20 -0800135 String authority, Bundle extras, boolean expedited) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700136 this.account = account;
137 this.syncSource = source;
138 this.authority = authority;
139 this.extras = extras != null ? new Bundle(extras) : extras;
Fred Quintana307da1a2010-01-21 14:24:20 -0800140 this.expedited = expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700141 this.authorityId = -1;
142 }
143
144 PendingOperation(PendingOperation other) {
145 this.account = other.account;
146 this.syncSource = other.syncSource;
147 this.authority = other.authority;
148 this.extras = other.extras;
149 this.authorityId = other.authorityId;
Fred Quintana307da1a2010-01-21 14:24:20 -0800150 this.expedited = other.expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700151 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 }
Costin Manolache360e4542009-09-04 13:36:04 -0700153
Dianne Hackborn231cc602009-04-27 17:10:36 -0700154 static class AccountInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700155 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700156 final HashMap<String, AuthorityInfo> authorities =
157 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700158
Dianne Hackborn7a135592009-05-06 00:28:37 -0700159 AccountInfo(Account account) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700160 this.account = account;
161 }
162 }
Costin Manolache360e4542009-09-04 13:36:04 -0700163
Dianne Hackborn231cc602009-04-27 17:10:36 -0700164 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700165 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700166 final String authority;
167 final int ident;
168 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700169 int syncable;
Fred Quintana307da1a2010-01-21 14:24:20 -0800170 long backoffTime;
171 long backoffDelay;
172 long delayUntil;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800173 final ArrayList<Pair<Bundle, Long>> periodicSyncs;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700174
Dianne Hackborn7a135592009-05-06 00:28:37 -0700175 AuthorityInfo(Account account, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700176 this.account = account;
177 this.authority = authority;
178 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700179 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700180 syncable = -1; // default to "unknown"
Fred Quintana307da1a2010-01-21 14:24:20 -0800181 backoffTime = -1; // if < 0 then we aren't in backoff mode
182 backoffDelay = -1; // if < 0 then we aren't in backoff mode
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800183 periodicSyncs = new ArrayList<Pair<Bundle, Long>>();
184 periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700185 }
186 }
Costin Manolache360e4542009-09-04 13:36:04 -0700187
Dianne Hackborn231cc602009-04-27 17:10:36 -0700188 public static class SyncHistoryItem {
189 int authorityId;
190 int historyId;
191 long eventTime;
192 long elapsedTime;
193 int source;
194 int event;
195 long upstreamActivity;
196 long downstreamActivity;
197 String mesg;
198 }
Costin Manolache360e4542009-09-04 13:36:04 -0700199
Dianne Hackborn231cc602009-04-27 17:10:36 -0700200 public static class DayStats {
201 public final int day;
202 public int successCount;
203 public long successTime;
204 public int failureCount;
205 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700206
Dianne Hackborn231cc602009-04-27 17:10:36 -0700207 public DayStats(int day) {
208 this.day = day;
209 }
210 }
Costin Manolache360e4542009-09-04 13:36:04 -0700211
Dianne Hackborn231cc602009-04-27 17:10:36 -0700212 // Primary list of all syncable authorities. Also our global lock.
213 private final SparseArray<AuthorityInfo> mAuthorities =
214 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700215
Dianne Hackborn7a135592009-05-06 00:28:37 -0700216 private final HashMap<Account, AccountInfo> mAccounts =
217 new HashMap<Account, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
Dianne Hackborn231cc602009-04-27 17:10:36 -0700219 private final ArrayList<PendingOperation> mPendingOperations =
220 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700221
Dianne Hackborn231cc602009-04-27 17:10:36 -0700222 private ActiveSyncInfo mActiveSync;
Costin Manolache360e4542009-09-04 13:36:04 -0700223
Dianne Hackborn231cc602009-04-27 17:10:36 -0700224 private final SparseArray<SyncStatusInfo> mSyncStatus =
225 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700226
Dianne Hackborn231cc602009-04-27 17:10:36 -0700227 private final ArrayList<SyncHistoryItem> mSyncHistory =
228 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700229
Dianne Hackborn231cc602009-04-27 17:10:36 -0700230 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
231 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700232
Dianne Hackborn231cc602009-04-27 17:10:36 -0700233 // We keep 4 weeks of stats.
234 private final DayStats[] mDayStats = new DayStats[7*4];
235 private final Calendar mCal;
236 private int mYear;
237 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700238
Dianne Hackborn231cc602009-04-27 17:10:36 -0700239 private final Context mContext;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800240
Dianne Hackborn231cc602009-04-27 17:10:36 -0700241 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700242
Dianne Hackborn231cc602009-04-27 17:10:36 -0700243 /**
244 * This file contains the core engine state: all accounts and the
245 * settings for them. It must never be lost, and should be changed
246 * infrequently, so it is stored as an XML file.
247 */
248 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700249
Dianne Hackborn231cc602009-04-27 17:10:36 -0700250 /**
251 * This file contains the current sync status. We would like to retain
252 * it across boots, but its loss is not the end of the world, so we store
253 * this information as binary data.
254 */
255 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700256
Dianne Hackborn231cc602009-04-27 17:10:36 -0700257 /**
258 * This file contains sync statistics. This is purely debugging information
259 * so is written infrequently and can be thrown away at any time.
260 */
261 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700262
Dianne Hackborn231cc602009-04-27 17:10:36 -0700263 /**
264 * This file contains the pending sync operations. It is a binary file,
265 * which must be updated every time an operation is added or removed,
266 * so we have special handling of it.
267 */
268 private final AtomicFile mPendingFile;
269 private static final int PENDING_FINISH_TO_WRITE = 4;
270 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700271
Dianne Hackborn231cc602009-04-27 17:10:36 -0700272 private int mNextHistoryId = 0;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700273 private boolean mMasterSyncAutomatically = true;
Costin Manolache360e4542009-09-04 13:36:04 -0700274
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800275 private SyncStorageEngine(Context context, File dataDir) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700278
Dianne Hackborn231cc602009-04-27 17:10:36 -0700279 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700280
Dianne Hackborn231cc602009-04-27 17:10:36 -0700281 File systemDir = new File(dataDir, "system");
282 File syncDir = new File(systemDir, "sync");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800283 syncDir.mkdirs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700284 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
285 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
286 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
287 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700288
Dianne Hackborn231cc602009-04-27 17:10:36 -0700289 readAccountInfoLocked();
290 readStatusLocked();
291 readPendingOperationsLocked();
292 readStatisticsLocked();
293 readLegacyAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295
296 public static SyncStorageEngine newTestInstance(Context context) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800297 return new SyncStorageEngine(context, context.getFilesDir());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299
300 public static void init(Context context) {
301 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800302 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800304 // This call will return the correct directory whether Encrypted File Systems is
305 // enabled or not.
306 File dataDir = Environment.getSecureDataDirectory();
307 sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 }
309
310 public static SyncStorageEngine getSingleton() {
311 if (sSyncStorageEngine == null) {
312 throw new IllegalStateException("not initialized");
313 }
314 return sSyncStorageEngine;
315 }
316
Dianne Hackborn231cc602009-04-27 17:10:36 -0700317 @Override public void handleMessage(Message msg) {
318 if (msg.what == MSG_WRITE_STATUS) {
319 synchronized (mAccounts) {
320 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700321 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700322 } else if (msg.what == MSG_WRITE_STATISTICS) {
323 synchronized (mAccounts) {
324 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
326 }
327 }
Costin Manolache360e4542009-09-04 13:36:04 -0700328
Dianne Hackborn231cc602009-04-27 17:10:36 -0700329 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
330 synchronized (mAuthorities) {
331 mChangeListeners.register(callback, mask);
332 }
333 }
Costin Manolache360e4542009-09-04 13:36:04 -0700334
Dianne Hackborn231cc602009-04-27 17:10:36 -0700335 public void removeStatusChangeListener(ISyncStatusObserver callback) {
336 synchronized (mAuthorities) {
337 mChangeListeners.unregister(callback);
338 }
339 }
Costin Manolache360e4542009-09-04 13:36:04 -0700340
Dianne Hackborn231cc602009-04-27 17:10:36 -0700341 private void reportChange(int which) {
342 ArrayList<ISyncStatusObserver> reports = null;
343 synchronized (mAuthorities) {
344 int i = mChangeListeners.beginBroadcast();
345 while (i > 0) {
346 i--;
347 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
348 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 continue;
350 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700351 if (reports == null) {
352 reports = new ArrayList<ISyncStatusObserver>(i);
353 }
354 reports.add(mChangeListeners.getBroadcastItem(i));
355 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700356 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700357 }
Costin Manolache360e4542009-09-04 13:36:04 -0700358
Dianne Hackborn231cc602009-04-27 17:10:36 -0700359 if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
Costin Manolache360e4542009-09-04 13:36:04 -0700360
Dianne Hackborn231cc602009-04-27 17:10:36 -0700361 if (reports != null) {
362 int i = reports.size();
363 while (i > 0) {
364 i--;
365 try {
366 reports.get(i).onStatusChanged(which);
367 } catch (RemoteException e) {
368 // The remote callback list will take care of this for us.
369 }
370 }
371 }
372 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700373
Fred Quintanaac9385e2009-06-22 18:00:59 -0700374 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700375 synchronized (mAuthorities) {
376 if (account != null) {
377 AuthorityInfo authority = getAuthorityLocked(account, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700378 "getSyncAutomatically");
379 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700380 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700381
Dianne Hackborn231cc602009-04-27 17:10:36 -0700382 int i = mAuthorities.size();
383 while (i > 0) {
384 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700385 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700386 if (authority.authority.equals(providerName)
387 && authority.enabled) {
388 return true;
389 }
390 }
391 return false;
392 }
393 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394
Fred Quintanaac9385e2009-06-22 18:00:59 -0700395 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700396 boolean wasEnabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700397 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700398 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
399 wasEnabled = authority.enabled;
400 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700401 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700403
404 if (!wasEnabled && sync) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800405 ContentResolver.requestSync(account, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700406 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700407 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 }
409
Fred Quintana5e787c42009-08-16 23:13:53 -0700410 public int getIsSyncable(Account account, String providerName) {
411 synchronized (mAuthorities) {
412 if (account != null) {
413 AuthorityInfo authority = getAuthorityLocked(account, providerName,
414 "getIsSyncable");
415 if (authority == null) {
416 return -1;
417 }
418 return authority.syncable;
419 }
420
421 int i = mAuthorities.size();
422 while (i > 0) {
423 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700424 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700425 if (authority.authority.equals(providerName)) {
426 return authority.syncable;
427 }
428 }
429 return -1;
430 }
431 }
432
433 public void setIsSyncable(Account account, String providerName, int syncable) {
434 int oldState;
Fred Quintanab763ab22009-08-18 18:07:30 -0700435 if (syncable > 1) {
436 syncable = 1;
437 } else if (syncable < -1) {
438 syncable = -1;
439 }
440 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700441 synchronized (mAuthorities) {
442 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
443 oldState = authority.syncable;
444 authority.syncable = syncable;
445 writeAccountInfoLocked();
446 }
447
448 if (oldState <= 0 && syncable > 0) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800449 ContentResolver.requestSync(account, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700450 }
451 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
452 }
453
Fred Quintana307da1a2010-01-21 14:24:20 -0800454 public Pair<Long, Long> getBackoff(Account account, String providerName) {
455 synchronized (mAuthorities) {
456 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getBackoff");
457 if (authority == null || authority.backoffTime < 0) {
458 return null;
459 }
460 return Pair.create(authority.backoffTime, authority.backoffDelay);
461 }
462 }
463
464 public void setBackoff(Account account, String providerName,
465 long nextSyncTime, long nextDelay) {
466 if (Log.isLoggable(TAG, Log.VERBOSE)) {
467 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
468 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
469 }
470 boolean changed = false;
471 synchronized (mAuthorities) {
472 if (account == null || providerName == null) {
473 for (AccountInfo accountInfo : mAccounts.values()) {
474 if (account != null && !account.equals(accountInfo.account)) continue;
475 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
476 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
477 continue;
478 }
479 if (authorityInfo.backoffTime != nextSyncTime
480 || authorityInfo.backoffDelay != nextDelay) {
481 authorityInfo.backoffTime = nextSyncTime;
482 authorityInfo.backoffDelay = nextDelay;
483 changed = true;
484 }
485 }
486 }
487 } else {
488 AuthorityInfo authority =
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800489 getOrCreateAuthorityLocked(account, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800490 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
491 return;
492 }
493 authority.backoffTime = nextSyncTime;
494 authority.backoffDelay = nextDelay;
495 changed = true;
496 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800497 }
498
499 if (changed) {
500 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
501 }
502 }
503
504 public void setDelayUntilTime(Account account, String providerName, long delayUntil) {
505 if (Log.isLoggable(TAG, Log.VERBOSE)) {
506 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
507 + " -> delayUntil " + delayUntil);
508 }
509 synchronized (mAuthorities) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800510 AuthorityInfo authority = getOrCreateAuthorityLocked(
511 account, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800512 if (authority.delayUntil == delayUntil) {
513 return;
514 }
515 authority.delayUntil = delayUntil;
Fred Quintana307da1a2010-01-21 14:24:20 -0800516 }
517
518 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
519 }
520
521 public long getDelayUntilTime(Account account, String providerName) {
522 synchronized (mAuthorities) {
523 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getDelayUntil");
524 if (authority == null) {
525 return 0;
526 }
527 return authority.delayUntil;
528 }
529 }
530
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800531 private void updateOrRemovePeriodicSync(Account account, String providerName, Bundle extras,
532 long period, boolean add) {
533 if (period <= 0) {
534 period = 0;
535 }
536 if (extras == null) {
537 extras = new Bundle();
538 }
539 if (Log.isLoggable(TAG, Log.VERBOSE)) {
540 Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", provider " + providerName
541 + " -> period " + period + ", extras " + extras);
542 }
543 synchronized (mAuthorities) {
544 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
545 if (add) {
546 boolean alreadyPresent = false;
547 for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
548 Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
549 final Bundle existingExtras = syncInfo.first;
550 if (equals(existingExtras, extras)) {
551 if (syncInfo.second == period) {
552 return;
553 }
554 authority.periodicSyncs.set(i, Pair.create(extras, period));
555 alreadyPresent = true;
556 break;
557 }
558 }
559 if (!alreadyPresent) {
560 authority.periodicSyncs.add(Pair.create(extras, period));
561 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
562 status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
563 }
564 } else {
565 SyncStatusInfo status = mSyncStatus.get(authority.ident);
566 boolean changed = false;
567 Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
568 int i = 0;
569 while (iterator.hasNext()) {
570 Pair<Bundle, Long> syncInfo = iterator.next();
571 if (equals(syncInfo.first, extras)) {
572 iterator.remove();
573 changed = true;
574 if (status != null) {
575 status.removePeriodicSyncTime(i);
576 }
577 } else {
578 i++;
579 }
580 }
581 if (!changed) {
582 return;
583 }
584 }
585 writeAccountInfoLocked();
586 writeStatusLocked();
587 }
588
589 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
590 }
591
592 public void addPeriodicSync(Account account, String providerName, Bundle extras,
593 long pollFrequency) {
594 updateOrRemovePeriodicSync(account, providerName, extras, pollFrequency, true /* add */);
595 }
596
597 public void removePeriodicSync(Account account, String providerName, Bundle extras) {
598 updateOrRemovePeriodicSync(account, providerName, extras, 0 /* period, ignored */,
599 false /* remove */);
600 }
601
602 public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
603 ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
604 synchronized (mAuthorities) {
605 AuthorityInfo authority = getAuthorityLocked(account, providerName, "getPeriodicSyncs");
606 if (authority != null) {
607 for (Pair<Bundle, Long> item : authority.periodicSyncs) {
608 syncs.add(new PeriodicSync(account, providerName, item.first, item.second));
609 }
610 }
611 }
612 return syncs;
613 }
614
Fred Quintanaac9385e2009-06-22 18:00:59 -0700615 public void setMasterSyncAutomatically(boolean flag) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700616 boolean old;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700617 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700618 old = mMasterSyncAutomatically;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700619 mMasterSyncAutomatically = flag;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700620 writeAccountInfoLocked();
621 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700622 if (!old && flag) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800623 ContentResolver.requestSync(null, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700624 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700625 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
626 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700627 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628
Fred Quintanaac9385e2009-06-22 18:00:59 -0700629 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700630 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700631 return mMasterSyncAutomatically;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700632 }
633 }
Costin Manolache360e4542009-09-04 13:36:04 -0700634
Dianne Hackborn7a135592009-05-06 00:28:37 -0700635 public AuthorityInfo getAuthority(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700636 synchronized (mAuthorities) {
637 return getAuthorityLocked(account, authority, null);
638 }
639 }
Costin Manolache360e4542009-09-04 13:36:04 -0700640
Dianne Hackborn231cc602009-04-27 17:10:36 -0700641 public AuthorityInfo getAuthority(int authorityId) {
642 synchronized (mAuthorities) {
643 return mAuthorities.get(authorityId);
644 }
645 }
Costin Manolache360e4542009-09-04 13:36:04 -0700646
Dianne Hackborn231cc602009-04-27 17:10:36 -0700647 /**
648 * Returns true if there is currently a sync operation for the given
649 * account or authority in the pending list, or actively being processed.
650 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700651 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700652 synchronized (mAuthorities) {
653 int i = mPendingOperations.size();
654 while (i > 0) {
655 i--;
656 // TODO(fredq): this probably shouldn't be considering
657 // pending operations.
658 PendingOperation op = mPendingOperations.get(i);
659 if (op.account.equals(account) && op.authority.equals(authority)) {
660 return true;
661 }
662 }
Costin Manolache360e4542009-09-04 13:36:04 -0700663
Dianne Hackborn231cc602009-04-27 17:10:36 -0700664 if (mActiveSync != null) {
665 AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
666 if (ainfo != null && ainfo.account.equals(account)
667 && ainfo.authority.equals(authority)) {
668 return true;
669 }
670 }
671 }
Costin Manolache360e4542009-09-04 13:36:04 -0700672
Dianne Hackborn231cc602009-04-27 17:10:36 -0700673 return false;
674 }
Costin Manolache360e4542009-09-04 13:36:04 -0700675
Dianne Hackborn231cc602009-04-27 17:10:36 -0700676 public PendingOperation insertIntoPending(PendingOperation op) {
677 synchronized (mAuthorities) {
678 if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
679 + " auth=" + op.authority
680 + " src=" + op.syncSource
681 + " extras=" + op.extras);
Costin Manolache360e4542009-09-04 13:36:04 -0700682
Dianne Hackborn231cc602009-04-27 17:10:36 -0700683 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
684 op.authority,
685 -1 /* desired identifier */,
686 true /* write accounts to storage */);
687 if (authority == null) {
688 return null;
689 }
Costin Manolache360e4542009-09-04 13:36:04 -0700690
Dianne Hackborn231cc602009-04-27 17:10:36 -0700691 op = new PendingOperation(op);
692 op.authorityId = authority.ident;
693 mPendingOperations.add(op);
694 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700695
Dianne Hackborn231cc602009-04-27 17:10:36 -0700696 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
697 status.pending = true;
698 }
Costin Manolache360e4542009-09-04 13:36:04 -0700699
Fred Quintanaac9385e2009-06-22 18:00:59 -0700700 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700701 return op;
702 }
703
704 public boolean deleteFromPending(PendingOperation op) {
705 boolean res = false;
706 synchronized (mAuthorities) {
707 if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
708 + " auth=" + op.authority
709 + " src=" + op.syncSource
710 + " extras=" + op.extras);
711 if (mPendingOperations.remove(op)) {
712 if (mPendingOperations.size() == 0
713 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
714 writePendingOperationsLocked();
715 mNumPendingFinished = 0;
716 } else {
717 mNumPendingFinished++;
718 }
Costin Manolache360e4542009-09-04 13:36:04 -0700719
Dianne Hackborn231cc602009-04-27 17:10:36 -0700720 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
721 "deleteFromPending");
722 if (authority != null) {
723 if (DEBUG) Log.v(TAG, "removing - " + authority);
724 final int N = mPendingOperations.size();
725 boolean morePending = false;
726 for (int i=0; i<N; i++) {
727 PendingOperation cur = mPendingOperations.get(i);
728 if (cur.account.equals(op.account)
729 && cur.authority.equals(op.authority)) {
730 morePending = true;
731 break;
732 }
733 }
Costin Manolache360e4542009-09-04 13:36:04 -0700734
Dianne Hackborn231cc602009-04-27 17:10:36 -0700735 if (!morePending) {
736 if (DEBUG) Log.v(TAG, "no more pending!");
737 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
738 status.pending = false;
739 }
740 }
Costin Manolache360e4542009-09-04 13:36:04 -0700741
Dianne Hackborn231cc602009-04-27 17:10:36 -0700742 res = true;
743 }
744 }
Costin Manolache360e4542009-09-04 13:36:04 -0700745
Fred Quintanaac9385e2009-06-22 18:00:59 -0700746 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700747 return res;
748 }
749
750 public int clearPending() {
751 int num;
752 synchronized (mAuthorities) {
753 if (DEBUG) Log.v(TAG, "clearPending");
754 num = mPendingOperations.size();
755 mPendingOperations.clear();
756 final int N = mSyncStatus.size();
757 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700758 mSyncStatus.valueAt(i).pending = false;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700759 }
760 writePendingOperationsLocked();
761 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700762 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700763 return num;
764 }
765
766 /**
767 * Return a copy of the current array of pending operations. The
768 * PendingOperation objects are the real objects stored inside, so that
769 * they can be used with deleteFromPending().
770 */
771 public ArrayList<PendingOperation> getPendingOperations() {
772 synchronized (mAuthorities) {
773 return new ArrayList<PendingOperation>(mPendingOperations);
774 }
775 }
Costin Manolache360e4542009-09-04 13:36:04 -0700776
Dianne Hackborn231cc602009-04-27 17:10:36 -0700777 /**
778 * Return the number of currently pending operations.
779 */
780 public int getPendingOperationCount() {
781 synchronized (mAuthorities) {
782 return mPendingOperations.size();
783 }
784 }
Costin Manolache360e4542009-09-04 13:36:04 -0700785
Dianne Hackborn231cc602009-04-27 17:10:36 -0700786 /**
787 * Called when the set of account has changed, given the new array of
788 * active accounts.
789 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700790 public void doDatabaseCleanup(Account[] accounts) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700791 synchronized (mAuthorities) {
792 if (DEBUG) Log.w(TAG, "Updating for new accounts...");
793 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
794 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
795 while (accIt.hasNext()) {
796 AccountInfo acc = accIt.next();
797 if (!ArrayUtils.contains(accounts, acc.account)) {
798 // This account no longer exists...
799 if (DEBUG) Log.w(TAG, "Account removed: " + acc.account);
800 for (AuthorityInfo auth : acc.authorities.values()) {
801 removing.put(auth.ident, auth);
802 }
803 accIt.remove();
804 }
805 }
Costin Manolache360e4542009-09-04 13:36:04 -0700806
Dianne Hackborn231cc602009-04-27 17:10:36 -0700807 // Clean out all data structures.
808 int i = removing.size();
809 if (i > 0) {
810 while (i > 0) {
811 i--;
812 int ident = removing.keyAt(i);
813 mAuthorities.remove(ident);
814 int j = mSyncStatus.size();
815 while (j > 0) {
816 j--;
817 if (mSyncStatus.keyAt(j) == ident) {
818 mSyncStatus.remove(mSyncStatus.keyAt(j));
819 }
820 }
821 j = mSyncHistory.size();
822 while (j > 0) {
823 j--;
824 if (mSyncHistory.get(j).authorityId == ident) {
825 mSyncHistory.remove(j);
826 }
827 }
828 }
829 writeAccountInfoLocked();
830 writeStatusLocked();
831 writePendingOperationsLocked();
832 writeStatisticsLocked();
833 }
834 }
835 }
836
837 /**
838 * Called when the currently active sync is changing (there can only be
839 * one at a time). Either supply a valid ActiveSyncContext with information
840 * about the sync, or null to stop the currently active sync.
841 */
842 public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
843 synchronized (mAuthorities) {
844 if (activeSyncContext != null) {
845 if (DEBUG) Log.v(TAG, "setActiveSync: account="
846 + activeSyncContext.mSyncOperation.account
847 + " auth=" + activeSyncContext.mSyncOperation.authority
848 + " src=" + activeSyncContext.mSyncOperation.syncSource
849 + " extras=" + activeSyncContext.mSyncOperation.extras);
850 if (mActiveSync != null) {
851 Log.w(TAG, "setActiveSync called with existing active sync!");
852 }
853 AuthorityInfo authority = getAuthorityLocked(
854 activeSyncContext.mSyncOperation.account,
855 activeSyncContext.mSyncOperation.authority,
856 "setActiveSync");
857 if (authority == null) {
858 return;
859 }
860 mActiveSync = new ActiveSyncInfo(authority.ident,
861 authority.account, authority.authority,
862 activeSyncContext.mStartTime);
863 } else {
864 if (DEBUG) Log.v(TAG, "setActiveSync: null");
865 mActiveSync = null;
866 }
867 }
Costin Manolache360e4542009-09-04 13:36:04 -0700868
Fred Quintanaac9385e2009-06-22 18:00:59 -0700869 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700870 }
871
872 /**
873 * To allow others to send active change reports, to poke clients.
874 */
875 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700876 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700877 }
Costin Manolache360e4542009-09-04 13:36:04 -0700878
Dianne Hackborn231cc602009-04-27 17:10:36 -0700879 /**
880 * Note that sync has started for the given account and authority.
881 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700882 public long insertStartSyncEvent(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700883 long now, int source) {
884 long id;
885 synchronized (mAuthorities) {
886 if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
887 + " auth=" + authorityName + " source=" + source);
888 AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
889 "insertStartSyncEvent");
890 if (authority == null) {
891 return -1;
892 }
893 SyncHistoryItem item = new SyncHistoryItem();
894 item.authorityId = authority.ident;
895 item.historyId = mNextHistoryId++;
896 if (mNextHistoryId < 0) mNextHistoryId = 0;
897 item.eventTime = now;
898 item.source = source;
899 item.event = EVENT_START;
900 mSyncHistory.add(0, item);
901 while (mSyncHistory.size() > MAX_HISTORY) {
902 mSyncHistory.remove(mSyncHistory.size()-1);
903 }
904 id = item.historyId;
905 if (DEBUG) Log.v(TAG, "returning historyId " + id);
906 }
Costin Manolache360e4542009-09-04 13:36:04 -0700907
Fred Quintanaac9385e2009-06-22 18:00:59 -0700908 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700909 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 }
911
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800912 public static boolean equals(Bundle b1, Bundle b2) {
913 if (b1.size() != b2.size()) {
914 return false;
915 }
916 if (b1.isEmpty()) {
917 return true;
918 }
919 for (String key : b1.keySet()) {
920 if (!b2.containsKey(key)) {
921 return false;
922 }
923 if (!b1.get(key).equals(b2.get(key))) {
924 return false;
925 }
926 }
927 return true;
928 }
929
930 public void stopSyncEvent(long historyId, Bundle extras, long elapsedTime, String resultMessage,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700932 synchronized (mAuthorities) {
933 if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
934 SyncHistoryItem item = null;
935 int i = mSyncHistory.size();
936 while (i > 0) {
937 i--;
938 item = mSyncHistory.get(i);
939 if (item.historyId == historyId) {
940 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700942 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 }
Costin Manolache360e4542009-09-04 13:36:04 -0700944
Dianne Hackborn231cc602009-04-27 17:10:36 -0700945 if (item == null) {
946 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
947 return;
948 }
Costin Manolache360e4542009-09-04 13:36:04 -0700949
Dianne Hackborn231cc602009-04-27 17:10:36 -0700950 item.elapsedTime = elapsedTime;
951 item.event = EVENT_STOP;
952 item.mesg = resultMessage;
953 item.downstreamActivity = downstreamActivity;
954 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -0700955
Dianne Hackborn231cc602009-04-27 17:10:36 -0700956 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -0700957
Dianne Hackborn231cc602009-04-27 17:10:36 -0700958 status.numSyncs++;
959 status.totalElapsedTime += elapsedTime;
960 switch (item.source) {
961 case SOURCE_LOCAL:
962 status.numSourceLocal++;
963 break;
964 case SOURCE_POLL:
965 status.numSourcePoll++;
966 break;
967 case SOURCE_USER:
968 status.numSourceUser++;
969 break;
970 case SOURCE_SERVER:
971 status.numSourceServer++;
972 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800973 case SOURCE_PERIODIC:
974 status.numSourcePeriodic++;
975 AuthorityInfo authority = mAuthorities.get(item.authorityId);
976 for (int periodicSyncIndex = 0;
977 periodicSyncIndex < authority.periodicSyncs.size();
978 periodicSyncIndex++) {
979 if (equals(extras, authority.periodicSyncs.get(periodicSyncIndex).first)) {
980 status.setPeriodicSyncTime(periodicSyncIndex, item.eventTime);
981 }
982 }
983 break;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700984 }
Costin Manolache360e4542009-09-04 13:36:04 -0700985
Dianne Hackborn231cc602009-04-27 17:10:36 -0700986 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700987 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700988 if (mDayStats[0] == null) {
989 mDayStats[0] = new DayStats(day);
990 } else if (day != mDayStats[0].day) {
991 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
992 mDayStats[0] = new DayStats(day);
993 writeStatisticsNow = true;
994 } else if (mDayStats[0] == null) {
995 }
996 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -0700997
Dianne Hackborn231cc602009-04-27 17:10:36 -0700998 final long lastSyncTime = (item.eventTime + elapsedTime);
999 boolean writeStatusNow = false;
1000 if (MESG_SUCCESS.equals(resultMessage)) {
1001 // - if successful, update the successful columns
1002 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
1003 writeStatusNow = true;
1004 }
1005 status.lastSuccessTime = lastSyncTime;
1006 status.lastSuccessSource = item.source;
1007 status.lastFailureTime = 0;
1008 status.lastFailureSource = -1;
1009 status.lastFailureMesg = null;
1010 status.initialFailureTime = 0;
1011 ds.successCount++;
1012 ds.successTime += elapsedTime;
1013 } else if (!MESG_CANCELED.equals(resultMessage)) {
1014 if (status.lastFailureTime == 0) {
1015 writeStatusNow = true;
1016 }
1017 status.lastFailureTime = lastSyncTime;
1018 status.lastFailureSource = item.source;
1019 status.lastFailureMesg = resultMessage;
1020 if (status.initialFailureTime == 0) {
1021 status.initialFailureTime = lastSyncTime;
1022 }
1023 ds.failureCount++;
1024 ds.failureTime += elapsedTime;
1025 }
Costin Manolache360e4542009-09-04 13:36:04 -07001026
Dianne Hackborn231cc602009-04-27 17:10:36 -07001027 if (writeStatusNow) {
1028 writeStatusLocked();
1029 } else if (!hasMessages(MSG_WRITE_STATUS)) {
1030 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
1031 WRITE_STATUS_DELAY);
1032 }
1033 if (writeStatisticsNow) {
1034 writeStatisticsLocked();
1035 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
1036 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
1037 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -07001038 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001039 }
Costin Manolache360e4542009-09-04 13:36:04 -07001040
Fred Quintanaac9385e2009-06-22 18:00:59 -07001041 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001042 }
1043
1044 /**
1045 * Return the currently active sync information, or null if there is no
1046 * active sync. Note that the returned object is the real, live active
1047 * sync object, so be careful what you do with it.
1048 */
1049 public ActiveSyncInfo getActiveSync() {
1050 synchronized (mAuthorities) {
1051 return mActiveSync;
1052 }
1053 }
Costin Manolache360e4542009-09-04 13:36:04 -07001054
Dianne Hackborn231cc602009-04-27 17:10:36 -07001055 /**
1056 * Return an array of the current sync status for all authorities. Note
1057 * that the objects inside the array are the real, live status objects,
1058 * so be careful what you do with them.
1059 */
1060 public ArrayList<SyncStatusInfo> getSyncStatus() {
1061 synchronized (mAuthorities) {
1062 final int N = mSyncStatus.size();
1063 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
1064 for (int i=0; i<N; i++) {
1065 ops.add(mSyncStatus.valueAt(i));
1066 }
1067 return ops;
1068 }
1069 }
Costin Manolache360e4542009-09-04 13:36:04 -07001070
Dianne Hackborn231cc602009-04-27 17:10:36 -07001071 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001072 * Return an array of the current authorities. Note
1073 * that the objects inside the array are the real, live objects,
1074 * so be careful what you do with them.
1075 */
1076 public ArrayList<AuthorityInfo> getAuthorities() {
1077 synchronized (mAuthorities) {
1078 final int N = mAuthorities.size();
1079 ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
1080 for (int i=0; i<N; i++) {
1081 infos.add(mAuthorities.valueAt(i));
1082 }
1083 return infos;
1084 }
1085 }
1086
1087 /**
Costin Manolacheb7520982009-09-02 18:03:05 -07001088 * Returns the status that matches the authority and account.
1089 *
1090 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -07001091 * @param authority the authority whose row should be selected
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001092 * @return the SyncStatusInfo for the authority
Dianne Hackborn231cc602009-04-27 17:10:36 -07001093 */
Costin Manolacheb7520982009-09-02 18:03:05 -07001094 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
1095 if (account == null || authority == null) {
1096 throw new IllegalArgumentException();
1097 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001098 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001099 final int N = mSyncStatus.size();
1100 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001101 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001102 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -07001103
1104 if (ainfo != null && ainfo.authority.equals(authority) &&
1105 account.equals(ainfo.account)) {
1106 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001107 }
1108 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001109 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001110 }
1111 }
Costin Manolache360e4542009-09-04 13:36:04 -07001112
Dianne Hackborn231cc602009-04-27 17:10:36 -07001113 /**
1114 * Return true if the pending status is true of any matching authorities.
1115 */
Fred Quintanaac9385e2009-06-22 18:00:59 -07001116 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001117 synchronized (mAuthorities) {
1118 final int N = mSyncStatus.size();
1119 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001120 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001121 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
1122 if (ainfo == null) {
1123 continue;
1124 }
1125 if (account != null && !ainfo.account.equals(account)) {
1126 continue;
1127 }
1128 if (ainfo.authority.equals(authority) && cur.pending) {
1129 return true;
1130 }
1131 }
1132 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 }
1134 }
1135
1136 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001137 * Return an array of the current sync status for all authorities. Note
1138 * that the objects inside the array are the real, live status objects,
1139 * so be careful what you do with them.
1140 */
1141 public ArrayList<SyncHistoryItem> getSyncHistory() {
1142 synchronized (mAuthorities) {
1143 final int N = mSyncHistory.size();
1144 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1145 for (int i=0; i<N; i++) {
1146 items.add(mSyncHistory.get(i));
1147 }
1148 return items;
1149 }
1150 }
Costin Manolache360e4542009-09-04 13:36:04 -07001151
Dianne Hackborn231cc602009-04-27 17:10:36 -07001152 /**
1153 * Return an array of the current per-day statistics. Note
1154 * that the objects inside the array are the real, live status objects,
1155 * so be careful what you do with them.
1156 */
1157 public DayStats[] getDayStatistics() {
1158 synchronized (mAuthorities) {
1159 DayStats[] ds = new DayStats[mDayStats.length];
1160 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1161 return ds;
1162 }
1163 }
Costin Manolache360e4542009-09-04 13:36:04 -07001164
Dianne Hackborn231cc602009-04-27 17:10:36 -07001165 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 * If sync is failing for any of the provider/accounts then determine the time at which it
1167 * started failing and return the earliest time over all the provider/accounts. If none are
1168 * failing then return 0.
1169 */
1170 public long getInitialSyncFailureTime() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001171 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001172 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001173 return 0;
1174 }
Costin Manolache360e4542009-09-04 13:36:04 -07001175
Dianne Hackborn231cc602009-04-27 17:10:36 -07001176 long oldest = 0;
1177 int i = mSyncStatus.size();
1178 while (i > 0) {
1179 i--;
1180 SyncStatusInfo stats = mSyncStatus.valueAt(i);
1181 AuthorityInfo authority = mAuthorities.get(stats.authorityId);
1182 if (authority != null && authority.enabled) {
1183 if (oldest == 0 || stats.initialFailureTime < oldest) {
1184 oldest = stats.initialFailureTime;
1185 }
1186 }
1187 }
Costin Manolache360e4542009-09-04 13:36:04 -07001188
Dianne Hackborn231cc602009-04-27 17:10:36 -07001189 return oldest;
1190 }
1191 }
Costin Manolache360e4542009-09-04 13:36:04 -07001192
Dianne Hackborn55280a92009-05-07 15:53:46 -07001193 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001194 mCal.setTimeInMillis(System.currentTimeMillis());
1195 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1196 if (mYear != mCal.get(Calendar.YEAR)) {
1197 mYear = mCal.get(Calendar.YEAR);
1198 mCal.clear();
1199 mCal.set(Calendar.YEAR, mYear);
1200 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1201 }
1202 return dayOfYear + mYearInDays;
1203 }
Costin Manolache360e4542009-09-04 13:36:04 -07001204
Dianne Hackborn231cc602009-04-27 17:10:36 -07001205 /**
1206 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001207 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001208 * @param accountName The name of the account for the authority.
1209 * @param authorityName The name of the authority itself.
1210 * @param tag If non-null, this will be used in a log message if the
1211 * requested authority does not exist.
1212 */
Dianne Hackborn7a135592009-05-06 00:28:37 -07001213 private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001214 String tag) {
1215 AccountInfo account = mAccounts.get(accountName);
1216 if (account == null) {
1217 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001218 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1219 Log.v(TAG, tag + ": unknown account " + accountName);
1220 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001221 }
1222 return null;
1223 }
1224 AuthorityInfo authority = account.authorities.get(authorityName);
1225 if (authority == null) {
1226 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001227 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1228 Log.v(TAG, tag + ": unknown authority " + authorityName);
1229 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001230 }
1231 return null;
1232 }
Costin Manolache360e4542009-09-04 13:36:04 -07001233
Dianne Hackborn231cc602009-04-27 17:10:36 -07001234 return authority;
1235 }
Costin Manolache360e4542009-09-04 13:36:04 -07001236
Dianne Hackborn7a135592009-05-06 00:28:37 -07001237 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001238 String authorityName, int ident, boolean doWrite) {
1239 AccountInfo account = mAccounts.get(accountName);
1240 if (account == null) {
1241 account = new AccountInfo(accountName);
1242 mAccounts.put(accountName, account);
1243 }
1244 AuthorityInfo authority = account.authorities.get(authorityName);
1245 if (authority == null) {
1246 if (ident < 0) {
1247 // Look for a new identifier for this authority.
1248 final int N = mAuthorities.size();
1249 ident = 0;
1250 for (int i=0; i<N; i++) {
1251 if (mAuthorities.valueAt(i).ident > ident) {
1252 break;
1253 }
1254 ident++;
1255 }
1256 }
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -07001257 if (DEBUG) Log.v(TAG, "created a new AuthorityInfo for " + accountName
Fred Quintanab763ab22009-08-18 18:07:30 -07001258 + ", provider " + authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001259 authority = new AuthorityInfo(accountName, authorityName, ident);
1260 account.authorities.put(authorityName, authority);
1261 mAuthorities.put(ident, authority);
1262 if (doWrite) {
1263 writeAccountInfoLocked();
1264 }
1265 }
Costin Manolache360e4542009-09-04 13:36:04 -07001266
Dianne Hackborn231cc602009-04-27 17:10:36 -07001267 return authority;
1268 }
Costin Manolache360e4542009-09-04 13:36:04 -07001269
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001270 public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
1271 synchronized (mAuthorities) {
1272 return getOrCreateSyncStatusLocked(authority.ident);
1273 }
1274 }
1275
Dianne Hackborn231cc602009-04-27 17:10:36 -07001276 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1277 SyncStatusInfo status = mSyncStatus.get(authorityId);
1278 if (status == null) {
1279 status = new SyncStatusInfo(authorityId);
1280 mSyncStatus.put(authorityId, status);
1281 }
1282 return status;
1283 }
Costin Manolache360e4542009-09-04 13:36:04 -07001284
Dianne Hackborn55280a92009-05-07 15:53:46 -07001285 public void writeAllState() {
1286 synchronized (mAuthorities) {
1287 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001288
Dianne Hackborn55280a92009-05-07 15:53:46 -07001289 if (mNumPendingFinished > 0) {
1290 // Only write these if they are out of date.
1291 writePendingOperationsLocked();
1292 }
Costin Manolache360e4542009-09-04 13:36:04 -07001293
Dianne Hackborn55280a92009-05-07 15:53:46 -07001294 // Just always write these... they are likely out of date.
1295 writeStatusLocked();
1296 writeStatisticsLocked();
1297 }
1298 }
Costin Manolache360e4542009-09-04 13:36:04 -07001299
Dianne Hackborn231cc602009-04-27 17:10:36 -07001300 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001301 * public for testing
1302 */
1303 public void clearAndReadState() {
1304 synchronized (mAuthorities) {
1305 mAuthorities.clear();
1306 mAccounts.clear();
1307 mPendingOperations.clear();
1308 mSyncStatus.clear();
1309 mSyncHistory.clear();
1310
1311 readAccountInfoLocked();
1312 readStatusLocked();
1313 readPendingOperationsLocked();
1314 readStatisticsLocked();
1315 readLegacyAccountInfoLocked();
1316 }
1317 }
1318
1319 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001320 * Read all account information back in to the initial engine state.
1321 */
1322 private void readAccountInfoLocked() {
1323 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001325 fis = mAccountInfoFile.openRead();
1326 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1327 XmlPullParser parser = Xml.newPullParser();
1328 parser.setInput(fis, null);
1329 int eventType = parser.getEventType();
1330 while (eventType != XmlPullParser.START_TAG) {
1331 eventType = parser.next();
1332 }
1333 String tagName = parser.getName();
1334 if ("accounts".equals(tagName)) {
1335 String listen = parser.getAttributeValue(
1336 null, "listen-for-tickles");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001337 mMasterSyncAutomatically = listen == null
Dianne Hackborn231cc602009-04-27 17:10:36 -07001338 || Boolean.parseBoolean(listen);
1339 eventType = parser.next();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001340 AuthorityInfo authority = null;
1341 Pair<Bundle, Long> periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001342 do {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001343 if (eventType == XmlPullParser.START_TAG) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001344 tagName = parser.getName();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001345 if (parser.getDepth() == 2) {
1346 if ("authority".equals(tagName)) {
1347 authority = parseAuthority(parser);
1348 periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001349 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001350 } else if (parser.getDepth() == 3) {
1351 if ("periodicSync".equals(tagName) && authority != null) {
1352 periodicSync = parsePeriodicSync(parser, authority);
1353 }
1354 } else if (parser.getDepth() == 4 && periodicSync != null) {
1355 if ("extra".equals(tagName)) {
1356 parseExtra(parser, periodicSync);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001357 }
1358 }
1359 }
1360 eventType = parser.next();
1361 } while (eventType != XmlPullParser.END_DOCUMENT);
1362 }
1363 } catch (XmlPullParserException e) {
1364 Log.w(TAG, "Error reading accounts", e);
1365 } catch (java.io.IOException e) {
1366 if (fis == null) Log.i(TAG, "No initial accounts");
1367 else Log.w(TAG, "Error reading accounts", e);
1368 } finally {
1369 if (fis != null) {
1370 try {
1371 fis.close();
1372 } catch (java.io.IOException e1) {
1373 }
1374 }
1375 }
1376 }
Costin Manolache360e4542009-09-04 13:36:04 -07001377
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001378 private AuthorityInfo parseAuthority(XmlPullParser parser) {
1379 AuthorityInfo authority = null;
1380 int id = -1;
1381 try {
1382 id = Integer.parseInt(parser.getAttributeValue(
1383 null, "id"));
1384 } catch (NumberFormatException e) {
1385 Log.e(TAG, "error parsing the id of the authority", e);
1386 } catch (NullPointerException e) {
1387 Log.e(TAG, "the id of the authority is null", e);
1388 }
1389 if (id >= 0) {
1390 String accountName = parser.getAttributeValue(null, "account");
1391 String accountType = parser.getAttributeValue(null, "type");
1392 if (accountType == null) {
1393 accountType = "com.google";
1394 }
1395 String authorityName = parser.getAttributeValue(null, "authority");
1396 String enabled = parser.getAttributeValue(null, "enabled");
1397 String syncable = parser.getAttributeValue(null, "syncable");
1398 authority = mAuthorities.get(id);
1399 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1400 + accountName + " auth=" + authorityName
1401 + " enabled=" + enabled
1402 + " syncable=" + syncable);
1403 if (authority == null) {
1404 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1405 authority = getOrCreateAuthorityLocked(
1406 new Account(accountName, accountType), authorityName, id, false);
1407 // clear this since we will read these later on
1408 authority.periodicSyncs.clear();
1409 }
1410 if (authority != null) {
1411 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
1412 if ("unknown".equals(syncable)) {
1413 authority.syncable = -1;
1414 } else {
1415 authority.syncable =
1416 (syncable == null || Boolean.parseBoolean(enabled)) ? 1 : 0;
1417 }
1418 } else {
1419 Log.w(TAG, "Failure adding authority: account="
1420 + accountName + " auth=" + authorityName
1421 + " enabled=" + enabled
1422 + " syncable=" + syncable);
1423 }
1424 }
1425
1426 return authority;
1427 }
1428
1429 private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
1430 Bundle extras = new Bundle();
1431 String periodValue = parser.getAttributeValue(null, "period");
1432 final long period;
1433 try {
1434 period = Long.parseLong(periodValue);
1435 } catch (NumberFormatException e) {
1436 Log.e(TAG, "error parsing the period of a periodic sync", e);
1437 return null;
1438 } catch (NullPointerException e) {
1439 Log.e(TAG, "the period of a periodic sync is null", e);
1440 return null;
1441 }
1442 final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
1443 authority.periodicSyncs.add(periodicSync);
1444 return periodicSync;
1445 }
1446
1447 private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
1448 final Bundle extras = periodicSync.first;
1449 String name = parser.getAttributeValue(null, "name");
1450 String type = parser.getAttributeValue(null, "type");
1451 String value1 = parser.getAttributeValue(null, "value1");
1452 String value2 = parser.getAttributeValue(null, "value2");
1453
1454 try {
1455 if ("long".equals(type)) {
1456 extras.putLong(name, Long.parseLong(value1));
1457 } else if ("integer".equals(type)) {
1458 extras.putInt(name, Integer.parseInt(value1));
1459 } else if ("double".equals(type)) {
1460 extras.putDouble(name, Double.parseDouble(value1));
1461 } else if ("float".equals(type)) {
1462 extras.putFloat(name, Float.parseFloat(value1));
1463 } else if ("boolean".equals(type)) {
1464 extras.putBoolean(name, Boolean.parseBoolean(value1));
1465 } else if ("string".equals(type)) {
1466 extras.putString(name, value1);
1467 } else if ("account".equals(type)) {
1468 extras.putParcelable(name, new Account(value1, value2));
1469 }
1470 } catch (NumberFormatException e) {
1471 Log.e(TAG, "error parsing bundle value", e);
1472 } catch (NullPointerException e) {
1473 Log.e(TAG, "error parsing bundle value", e);
1474 }
1475 }
1476
Dianne Hackborn231cc602009-04-27 17:10:36 -07001477 /**
1478 * Write all account information to the account file.
1479 */
1480 private void writeAccountInfoLocked() {
1481 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1482 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001483
Dianne Hackborn231cc602009-04-27 17:10:36 -07001484 try {
1485 fos = mAccountInfoFile.startWrite();
1486 XmlSerializer out = new FastXmlSerializer();
1487 out.setOutput(fos, "utf-8");
1488 out.startDocument(null, true);
1489 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001490
Dianne Hackborn231cc602009-04-27 17:10:36 -07001491 out.startTag(null, "accounts");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001492 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001493 out.attribute(null, "listen-for-tickles", "false");
1494 }
Costin Manolache360e4542009-09-04 13:36:04 -07001495
Dianne Hackborn231cc602009-04-27 17:10:36 -07001496 final int N = mAuthorities.size();
1497 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001498 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001499 out.startTag(null, "authority");
1500 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001501 out.attribute(null, "account", authority.account.name);
1502 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001503 out.attribute(null, "authority", authority.authority);
1504 if (!authority.enabled) {
1505 out.attribute(null, "enabled", "false");
1506 }
Fred Quintana5e787c42009-08-16 23:13:53 -07001507 if (authority.syncable < 0) {
1508 out.attribute(null, "syncable", "unknown");
1509 } else if (authority.syncable == 0) {
1510 out.attribute(null, "syncable", "false");
1511 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001512 for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
1513 out.startTag(null, "periodicSync");
1514 out.attribute(null, "period", Long.toString(periodicSync.second));
1515 final Bundle extras = periodicSync.first;
1516 for (String key : extras.keySet()) {
1517 out.startTag(null, "extra");
1518 out.attribute(null, "name", key);
1519 final Object value = extras.get(key);
1520 if (value instanceof Long) {
1521 out.attribute(null, "type", "long");
1522 out.attribute(null, "value1", value.toString());
1523 } else if (value instanceof Integer) {
1524 out.attribute(null, "type", "integer");
1525 out.attribute(null, "value1", value.toString());
1526 } else if (value instanceof Boolean) {
1527 out.attribute(null, "type", "boolean");
1528 out.attribute(null, "value1", value.toString());
1529 } else if (value instanceof Float) {
1530 out.attribute(null, "type", "float");
1531 out.attribute(null, "value1", value.toString());
1532 } else if (value instanceof Double) {
1533 out.attribute(null, "type", "double");
1534 out.attribute(null, "value1", value.toString());
1535 } else if (value instanceof String) {
1536 out.attribute(null, "type", "string");
1537 out.attribute(null, "value1", value.toString());
1538 } else if (value instanceof Account) {
1539 out.attribute(null, "type", "account");
1540 out.attribute(null, "value1", ((Account)value).name);
1541 out.attribute(null, "value2", ((Account)value).type);
1542 }
1543 out.endTag(null, "extra");
1544 }
1545 out.endTag(null, "periodicSync");
1546 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001547 out.endTag(null, "authority");
1548 }
Costin Manolache360e4542009-09-04 13:36:04 -07001549
Dianne Hackborn231cc602009-04-27 17:10:36 -07001550 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001551
Dianne Hackborn231cc602009-04-27 17:10:36 -07001552 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001553
Dianne Hackborn231cc602009-04-27 17:10:36 -07001554 mAccountInfoFile.finishWrite(fos);
1555 } catch (java.io.IOException e1) {
1556 Log.w(TAG, "Error writing accounts", e1);
1557 if (fos != null) {
1558 mAccountInfoFile.failWrite(fos);
1559 }
1560 }
1561 }
Costin Manolache360e4542009-09-04 13:36:04 -07001562
Dianne Hackborn231cc602009-04-27 17:10:36 -07001563 static int getIntColumn(Cursor c, String name) {
1564 return c.getInt(c.getColumnIndex(name));
1565 }
Costin Manolache360e4542009-09-04 13:36:04 -07001566
Dianne Hackborn231cc602009-04-27 17:10:36 -07001567 static long getLongColumn(Cursor c, String name) {
1568 return c.getLong(c.getColumnIndex(name));
1569 }
Costin Manolache360e4542009-09-04 13:36:04 -07001570
Dianne Hackborn231cc602009-04-27 17:10:36 -07001571 /**
1572 * Load sync engine state from the old syncmanager database, and then
1573 * erase it. Note that we don't deal with pending operations, active
1574 * sync, or history.
1575 */
1576 private void readLegacyAccountInfoLocked() {
1577 // Look for old database to initialize from.
1578 File file = mContext.getDatabasePath("syncmanager.db");
1579 if (!file.exists()) {
1580 return;
1581 }
1582 String path = file.getPath();
1583 SQLiteDatabase db = null;
1584 try {
1585 db = SQLiteDatabase.openDatabase(path, null,
1586 SQLiteDatabase.OPEN_READONLY);
1587 } catch (SQLiteException e) {
1588 }
Costin Manolache360e4542009-09-04 13:36:04 -07001589
Dianne Hackborn231cc602009-04-27 17:10:36 -07001590 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001591 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001592
Dianne Hackborn231cc602009-04-27 17:10:36 -07001593 // Copy in all of the status information, as well as accounts.
1594 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1595 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1596 qb.setTables("stats, status");
1597 HashMap<String,String> map = new HashMap<String,String>();
1598 map.put("_id", "status._id as _id");
1599 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001600 if (hasType) {
1601 map.put("account_type", "stats.account_type as account_type");
1602 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001603 map.put("authority", "stats.authority as authority");
1604 map.put("totalElapsedTime", "totalElapsedTime");
1605 map.put("numSyncs", "numSyncs");
1606 map.put("numSourceLocal", "numSourceLocal");
1607 map.put("numSourcePoll", "numSourcePoll");
1608 map.put("numSourceServer", "numSourceServer");
1609 map.put("numSourceUser", "numSourceUser");
1610 map.put("lastSuccessSource", "lastSuccessSource");
1611 map.put("lastSuccessTime", "lastSuccessTime");
1612 map.put("lastFailureSource", "lastFailureSource");
1613 map.put("lastFailureTime", "lastFailureTime");
1614 map.put("lastFailureMesg", "lastFailureMesg");
1615 map.put("pending", "pending");
1616 qb.setProjectionMap(map);
1617 qb.appendWhere("stats._id = status.stats_id");
1618 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001620 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001621 String accountType = hasType
1622 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001623 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001624 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001626 String authorityName = c.getString(c.getColumnIndex("authority"));
1627 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001628 new Account(accountName, accountType),
1629 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001630 if (authority != null) {
1631 int i = mSyncStatus.size();
1632 boolean found = false;
1633 SyncStatusInfo st = null;
1634 while (i > 0) {
1635 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001636 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001637 if (st.authorityId == authority.ident) {
1638 found = true;
1639 break;
1640 }
1641 }
1642 if (!found) {
1643 st = new SyncStatusInfo(authority.ident);
1644 mSyncStatus.put(authority.ident, st);
1645 }
1646 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1647 st.numSyncs = getIntColumn(c, "numSyncs");
1648 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1649 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1650 st.numSourceServer = getIntColumn(c, "numSourceServer");
1651 st.numSourceUser = getIntColumn(c, "numSourceUser");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001652 st.numSourcePeriodic = 0;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001653 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1654 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1655 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1656 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1657 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1658 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
Costin Manolache360e4542009-09-04 13:36:04 -07001661
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001663
Dianne Hackborn231cc602009-04-27 17:10:36 -07001664 // Retrieve the settings.
1665 qb = new SQLiteQueryBuilder();
1666 qb.setTables("settings");
1667 c = qb.query(db, null, null, null, null, null, null);
1668 while (c.moveToNext()) {
1669 String name = c.getString(c.getColumnIndex("name"));
1670 String value = c.getString(c.getColumnIndex("value"));
1671 if (name == null) continue;
1672 if (name.equals("listen_for_tickles")) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001673 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001674 } else if (name.startsWith("sync_provider_")) {
1675 String provider = name.substring("sync_provider_".length(),
1676 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001677 int i = mAuthorities.size();
1678 while (i > 0) {
1679 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001680 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001681 if (authority.authority.equals(provider)) {
1682 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001683 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001684 }
1685 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001686 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 }
Costin Manolache360e4542009-09-04 13:36:04 -07001688
Dianne Hackborn231cc602009-04-27 17:10:36 -07001689 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001690
Dianne Hackborn231cc602009-04-27 17:10:36 -07001691 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001692
Dianne Hackborn231cc602009-04-27 17:10:36 -07001693 writeAccountInfoLocked();
1694 writeStatusLocked();
1695 (new File(path)).delete();
1696 }
1697 }
Costin Manolache360e4542009-09-04 13:36:04 -07001698
Dianne Hackborn231cc602009-04-27 17:10:36 -07001699 public static final int STATUS_FILE_END = 0;
1700 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001701
Dianne Hackborn231cc602009-04-27 17:10:36 -07001702 /**
1703 * Read all sync status back in to the initial engine state.
1704 */
1705 private void readStatusLocked() {
1706 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1707 try {
1708 byte[] data = mStatusFile.readFully();
1709 Parcel in = Parcel.obtain();
1710 in.unmarshall(data, 0, data.length);
1711 in.setDataPosition(0);
1712 int token;
1713 while ((token=in.readInt()) != STATUS_FILE_END) {
1714 if (token == STATUS_FILE_ITEM) {
1715 SyncStatusInfo status = new SyncStatusInfo(in);
1716 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1717 status.pending = false;
1718 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1719 + status.authorityId);
1720 mSyncStatus.put(status.authorityId, status);
1721 }
1722 } else {
1723 // Ooops.
1724 Log.w(TAG, "Unknown status token: " + token);
1725 break;
1726 }
1727 }
1728 } catch (java.io.IOException e) {
1729 Log.i(TAG, "No initial status");
1730 }
1731 }
Costin Manolache360e4542009-09-04 13:36:04 -07001732
Dianne Hackborn231cc602009-04-27 17:10:36 -07001733 /**
1734 * Write all sync status to the sync status file.
1735 */
1736 private void writeStatusLocked() {
1737 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001738
Dianne Hackborn231cc602009-04-27 17:10:36 -07001739 // The file is being written, so we don't need to have a scheduled
1740 // write until the next change.
1741 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001742
Dianne Hackborn231cc602009-04-27 17:10:36 -07001743 FileOutputStream fos = null;
1744 try {
1745 fos = mStatusFile.startWrite();
1746 Parcel out = Parcel.obtain();
1747 final int N = mSyncStatus.size();
1748 for (int i=0; i<N; i++) {
1749 SyncStatusInfo status = mSyncStatus.valueAt(i);
1750 out.writeInt(STATUS_FILE_ITEM);
1751 status.writeToParcel(out, 0);
1752 }
1753 out.writeInt(STATUS_FILE_END);
1754 fos.write(out.marshall());
1755 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001756
Dianne Hackborn231cc602009-04-27 17:10:36 -07001757 mStatusFile.finishWrite(fos);
1758 } catch (java.io.IOException e1) {
1759 Log.w(TAG, "Error writing status", e1);
1760 if (fos != null) {
1761 mStatusFile.failWrite(fos);
1762 }
1763 }
1764 }
Costin Manolache360e4542009-09-04 13:36:04 -07001765
Fred Quintana307da1a2010-01-21 14:24:20 -08001766 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07001767
Dianne Hackborn231cc602009-04-27 17:10:36 -07001768 /**
1769 * Read all pending operations back in to the initial engine state.
1770 */
1771 private void readPendingOperationsLocked() {
1772 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1773 try {
1774 byte[] data = mPendingFile.readFully();
1775 Parcel in = Parcel.obtain();
1776 in.unmarshall(data, 0, data.length);
1777 in.setDataPosition(0);
1778 final int SIZE = in.dataSize();
1779 while (in.dataPosition() < SIZE) {
1780 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08001781 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001782 Log.w(TAG, "Unknown pending operation version "
1783 + version + "; dropping all ops");
1784 break;
1785 }
1786 int authorityId = in.readInt();
1787 int syncSource = in.readInt();
1788 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08001789 boolean expedited;
1790 if (version == PENDING_OPERATION_VERSION) {
1791 expedited = in.readInt() != 0;
1792 } else {
1793 expedited = false;
1794 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001795 AuthorityInfo authority = mAuthorities.get(authorityId);
1796 if (authority != null) {
1797 Bundle extras = null;
1798 if (flatExtras != null) {
1799 extras = unflattenBundle(flatExtras);
1800 }
1801 PendingOperation op = new PendingOperation(
1802 authority.account, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08001803 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001804 op.authorityId = authorityId;
1805 op.flatExtras = flatExtras;
1806 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
1807 + " auth=" + op.authority
1808 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08001809 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07001810 + " extras=" + op.extras);
1811 mPendingOperations.add(op);
1812 }
1813 }
1814 } catch (java.io.IOException e) {
1815 Log.i(TAG, "No initial pending operations");
1816 }
1817 }
Costin Manolache360e4542009-09-04 13:36:04 -07001818
Dianne Hackborn231cc602009-04-27 17:10:36 -07001819 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
1820 out.writeInt(PENDING_OPERATION_VERSION);
1821 out.writeInt(op.authorityId);
1822 out.writeInt(op.syncSource);
1823 if (op.flatExtras == null && op.extras != null) {
1824 op.flatExtras = flattenBundle(op.extras);
1825 }
1826 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08001827 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001828 }
Costin Manolache360e4542009-09-04 13:36:04 -07001829
Dianne Hackborn231cc602009-04-27 17:10:36 -07001830 /**
1831 * Write all currently pending ops to the pending ops file.
1832 */
1833 private void writePendingOperationsLocked() {
1834 final int N = mPendingOperations.size();
1835 FileOutputStream fos = null;
1836 try {
1837 if (N == 0) {
1838 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
1839 mPendingFile.truncate();
1840 return;
1841 }
Costin Manolache360e4542009-09-04 13:36:04 -07001842
Dianne Hackborn231cc602009-04-27 17:10:36 -07001843 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
1844 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07001845
Dianne Hackborn231cc602009-04-27 17:10:36 -07001846 Parcel out = Parcel.obtain();
1847 for (int i=0; i<N; i++) {
1848 PendingOperation op = mPendingOperations.get(i);
1849 writePendingOperationLocked(op, out);
1850 }
1851 fos.write(out.marshall());
1852 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001853
Dianne Hackborn231cc602009-04-27 17:10:36 -07001854 mPendingFile.finishWrite(fos);
1855 } catch (java.io.IOException e1) {
1856 Log.w(TAG, "Error writing pending operations", e1);
1857 if (fos != null) {
1858 mPendingFile.failWrite(fos);
1859 }
1860 }
1861 }
Costin Manolache360e4542009-09-04 13:36:04 -07001862
Dianne Hackborn231cc602009-04-27 17:10:36 -07001863 /**
1864 * Append the given operation to the pending ops file; if unable to,
1865 * write all pending ops.
1866 */
1867 private void appendPendingOperationLocked(PendingOperation op) {
1868 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
1869 FileOutputStream fos = null;
1870 try {
1871 fos = mPendingFile.openAppend();
1872 } catch (java.io.IOException e) {
1873 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
1874 writePendingOperationsLocked();
1875 return;
1876 }
Costin Manolache360e4542009-09-04 13:36:04 -07001877
Dianne Hackborn231cc602009-04-27 17:10:36 -07001878 try {
1879 Parcel out = Parcel.obtain();
1880 writePendingOperationLocked(op, out);
1881 fos.write(out.marshall());
1882 out.recycle();
1883 } catch (java.io.IOException e1) {
1884 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001886 try {
1887 fos.close();
1888 } catch (java.io.IOException e2) {
1889 }
1890 }
1891 }
Costin Manolache360e4542009-09-04 13:36:04 -07001892
Dianne Hackborn231cc602009-04-27 17:10:36 -07001893 static private byte[] flattenBundle(Bundle bundle) {
1894 byte[] flatData = null;
1895 Parcel parcel = Parcel.obtain();
1896 try {
1897 bundle.writeToParcel(parcel, 0);
1898 flatData = parcel.marshall();
1899 } finally {
1900 parcel.recycle();
1901 }
1902 return flatData;
1903 }
Costin Manolache360e4542009-09-04 13:36:04 -07001904
Dianne Hackborn231cc602009-04-27 17:10:36 -07001905 static private Bundle unflattenBundle(byte[] flatData) {
1906 Bundle bundle;
1907 Parcel parcel = Parcel.obtain();
1908 try {
1909 parcel.unmarshall(flatData, 0, flatData.length);
1910 parcel.setDataPosition(0);
1911 bundle = parcel.readBundle();
1912 } catch (RuntimeException e) {
1913 // A RuntimeException is thrown if we were unable to parse the parcel.
1914 // Create an empty parcel in this case.
1915 bundle = new Bundle();
1916 } finally {
1917 parcel.recycle();
1918 }
1919 return bundle;
1920 }
Costin Manolache360e4542009-09-04 13:36:04 -07001921
Dianne Hackborn231cc602009-04-27 17:10:36 -07001922 public static final int STATISTICS_FILE_END = 0;
1923 public static final int STATISTICS_FILE_ITEM_OLD = 100;
1924 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07001925
Dianne Hackborn231cc602009-04-27 17:10:36 -07001926 /**
1927 * Read all sync statistics back in to the initial engine state.
1928 */
1929 private void readStatisticsLocked() {
1930 try {
1931 byte[] data = mStatisticsFile.readFully();
1932 Parcel in = Parcel.obtain();
1933 in.unmarshall(data, 0, data.length);
1934 in.setDataPosition(0);
1935 int token;
1936 int index = 0;
1937 while ((token=in.readInt()) != STATISTICS_FILE_END) {
1938 if (token == STATISTICS_FILE_ITEM
1939 || token == STATISTICS_FILE_ITEM_OLD) {
1940 int day = in.readInt();
1941 if (token == STATISTICS_FILE_ITEM_OLD) {
1942 day = day - 2009 + 14245; // Magic!
1943 }
1944 DayStats ds = new DayStats(day);
1945 ds.successCount = in.readInt();
1946 ds.successTime = in.readLong();
1947 ds.failureCount = in.readInt();
1948 ds.failureTime = in.readLong();
1949 if (index < mDayStats.length) {
1950 mDayStats[index] = ds;
1951 index++;
1952 }
1953 } else {
1954 // Ooops.
1955 Log.w(TAG, "Unknown stats token: " + token);
1956 break;
1957 }
1958 }
1959 } catch (java.io.IOException e) {
1960 Log.i(TAG, "No initial statistics");
1961 }
1962 }
Costin Manolache360e4542009-09-04 13:36:04 -07001963
Dianne Hackborn231cc602009-04-27 17:10:36 -07001964 /**
1965 * Write all sync statistics to the sync status file.
1966 */
1967 private void writeStatisticsLocked() {
1968 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001969
Dianne Hackborn231cc602009-04-27 17:10:36 -07001970 // The file is being written, so we don't need to have a scheduled
1971 // write until the next change.
1972 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07001973
Dianne Hackborn231cc602009-04-27 17:10:36 -07001974 FileOutputStream fos = null;
1975 try {
1976 fos = mStatisticsFile.startWrite();
1977 Parcel out = Parcel.obtain();
1978 final int N = mDayStats.length;
1979 for (int i=0; i<N; i++) {
1980 DayStats ds = mDayStats[i];
1981 if (ds == null) {
1982 break;
1983 }
1984 out.writeInt(STATISTICS_FILE_ITEM);
1985 out.writeInt(ds.day);
1986 out.writeInt(ds.successCount);
1987 out.writeLong(ds.successTime);
1988 out.writeInt(ds.failureCount);
1989 out.writeLong(ds.failureTime);
1990 }
1991 out.writeInt(STATISTICS_FILE_END);
1992 fos.write(out.marshall());
1993 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001994
Dianne Hackborn231cc602009-04-27 17:10:36 -07001995 mStatisticsFile.finishWrite(fos);
1996 } catch (java.io.IOException e1) {
1997 Log.w(TAG, "Error writing stats", e1);
1998 if (fos != null) {
1999 mStatisticsFile.failWrite(fos);
2000 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 }
2002 }
2003}