blob: fb2608a33334ec68e90b630d98b8618480483255 [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;
21import com.android.internal.util.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022
Dianne Hackborn231cc602009-04-27 17:10:36 -070023import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25import org.xmlpull.v1.XmlSerializer;
26
Fred Quintanad9d2f112009-04-23 13:36:27 -070027import android.accounts.Account;
Amith Yamasani70c874b2009-07-06 14:53:25 -070028import android.backup.IBackupManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.database.sqlite.SQLiteDatabase;
Dianne Hackborn231cc602009-04-27 17:10:36 -070031import android.database.sqlite.SQLiteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.database.sqlite.SQLiteQueryBuilder;
Dianne Hackborn231cc602009-04-27 17:10:36 -070033import android.os.Bundle;
34import android.os.Environment;
35import android.os.Handler;
36import android.os.Message;
37import android.os.Parcel;
38import android.os.RemoteCallbackList;
39import android.os.RemoteException;
Amith Yamasani70c874b2009-07-06 14:53:25 -070040import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.Log;
Dianne Hackborn231cc602009-04-27 17:10:36 -070042import android.util.SparseArray;
43import android.util.Xml;
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 Quintanaac9385e2009-06-22 18:00:59 -070092 private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
93 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
94
Dianne Hackborn231cc602009-04-27 17:10:36 -070095 // TODO: i18n -- grab these out of resources.
96 /** String names for the sync source types. */
97 public static final String[] SOURCES = { "SERVER",
98 "LOCAL",
99 "POLL",
100 "USER" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Dianne Hackborn231cc602009-04-27 17:10:36 -0700102 // The MESG column will contain one of these or one of the Error types.
103 public static final String MESG_SUCCESS = "success";
104 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700106 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700107
Dianne Hackborn231cc602009-04-27 17:10:36 -0700108 private static final int MSG_WRITE_STATUS = 1;
109 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700110
Dianne Hackborn231cc602009-04-27 17:10:36 -0700111 private static final int MSG_WRITE_STATISTICS = 2;
112 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700113
114 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700115
Dianne Hackborn231cc602009-04-27 17:10:36 -0700116 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700117 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700118 final int syncSource;
119 final String authority;
120 final Bundle extras; // note: read-only.
Costin Manolache360e4542009-09-04 13:36:04 -0700121
Dianne Hackborn231cc602009-04-27 17:10:36 -0700122 int authorityId;
123 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700124
Dianne Hackborn7a135592009-05-06 00:28:37 -0700125 PendingOperation(Account account, int source,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700126 String authority, Bundle extras) {
127 this.account = account;
128 this.syncSource = source;
129 this.authority = authority;
130 this.extras = extras != null ? new Bundle(extras) : extras;
131 this.authorityId = -1;
132 }
133
134 PendingOperation(PendingOperation other) {
135 this.account = other.account;
136 this.syncSource = other.syncSource;
137 this.authority = other.authority;
138 this.extras = other.extras;
139 this.authorityId = other.authorityId;
140 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 }
Costin Manolache360e4542009-09-04 13:36:04 -0700142
Dianne Hackborn231cc602009-04-27 17:10:36 -0700143 static class AccountInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700144 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700145 final HashMap<String, AuthorityInfo> authorities =
146 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700147
Dianne Hackborn7a135592009-05-06 00:28:37 -0700148 AccountInfo(Account account) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700149 this.account = account;
150 }
151 }
Costin Manolache360e4542009-09-04 13:36:04 -0700152
Dianne Hackborn231cc602009-04-27 17:10:36 -0700153 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700154 final Account account;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700155 final String authority;
156 final int ident;
157 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700158 int syncable;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700159
Dianne Hackborn7a135592009-05-06 00:28:37 -0700160 AuthorityInfo(Account account, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700161 this.account = account;
162 this.authority = authority;
163 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700164 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700165 syncable = -1; // default to "unknown"
Dianne Hackborn231cc602009-04-27 17:10:36 -0700166 }
167 }
Costin Manolache360e4542009-09-04 13:36:04 -0700168
Dianne Hackborn231cc602009-04-27 17:10:36 -0700169 public static class SyncHistoryItem {
170 int authorityId;
171 int historyId;
172 long eventTime;
173 long elapsedTime;
174 int source;
175 int event;
176 long upstreamActivity;
177 long downstreamActivity;
178 String mesg;
179 }
Costin Manolache360e4542009-09-04 13:36:04 -0700180
Dianne Hackborn231cc602009-04-27 17:10:36 -0700181 public static class DayStats {
182 public final int day;
183 public int successCount;
184 public long successTime;
185 public int failureCount;
186 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700187
Dianne Hackborn231cc602009-04-27 17:10:36 -0700188 public DayStats(int day) {
189 this.day = day;
190 }
191 }
Costin Manolache360e4542009-09-04 13:36:04 -0700192
Dianne Hackborn231cc602009-04-27 17:10:36 -0700193 // Primary list of all syncable authorities. Also our global lock.
194 private final SparseArray<AuthorityInfo> mAuthorities =
195 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700196
Dianne Hackborn7a135592009-05-06 00:28:37 -0700197 private final HashMap<Account, AccountInfo> mAccounts =
198 new HashMap<Account, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199
Dianne Hackborn231cc602009-04-27 17:10:36 -0700200 private final ArrayList<PendingOperation> mPendingOperations =
201 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700202
Dianne Hackborn231cc602009-04-27 17:10:36 -0700203 private ActiveSyncInfo mActiveSync;
Costin Manolache360e4542009-09-04 13:36:04 -0700204
Dianne Hackborn231cc602009-04-27 17:10:36 -0700205 private final SparseArray<SyncStatusInfo> mSyncStatus =
206 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700207
Dianne Hackborn231cc602009-04-27 17:10:36 -0700208 private final ArrayList<SyncHistoryItem> mSyncHistory =
209 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700210
Dianne Hackborn231cc602009-04-27 17:10:36 -0700211 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
212 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700213
Dianne Hackborn231cc602009-04-27 17:10:36 -0700214 // We keep 4 weeks of stats.
215 private final DayStats[] mDayStats = new DayStats[7*4];
216 private final Calendar mCal;
217 private int mYear;
218 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700219
Dianne Hackborn231cc602009-04-27 17:10:36 -0700220 private final Context mContext;
221 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700222
Dianne Hackborn231cc602009-04-27 17:10:36 -0700223 /**
224 * This file contains the core engine state: all accounts and the
225 * settings for them. It must never be lost, and should be changed
226 * infrequently, so it is stored as an XML file.
227 */
228 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700229
Dianne Hackborn231cc602009-04-27 17:10:36 -0700230 /**
231 * This file contains the current sync status. We would like to retain
232 * it across boots, but its loss is not the end of the world, so we store
233 * this information as binary data.
234 */
235 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700236
Dianne Hackborn231cc602009-04-27 17:10:36 -0700237 /**
238 * This file contains sync statistics. This is purely debugging information
239 * so is written infrequently and can be thrown away at any time.
240 */
241 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700242
Dianne Hackborn231cc602009-04-27 17:10:36 -0700243 /**
244 * This file contains the pending sync operations. It is a binary file,
245 * which must be updated every time an operation is added or removed,
246 * so we have special handling of it.
247 */
248 private final AtomicFile mPendingFile;
249 private static final int PENDING_FINISH_TO_WRITE = 4;
250 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700251
Dianne Hackborn231cc602009-04-27 17:10:36 -0700252 private int mNextHistoryId = 0;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700253 private boolean mMasterSyncAutomatically = true;
Costin Manolache360e4542009-09-04 13:36:04 -0700254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 private SyncStorageEngine(Context context) {
256 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700258
Dianne Hackborn231cc602009-04-27 17:10:36 -0700259 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700260
Dianne Hackborn231cc602009-04-27 17:10:36 -0700261 File dataDir = Environment.getDataDirectory();
262 File systemDir = new File(dataDir, "system");
263 File syncDir = new File(systemDir, "sync");
264 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
265 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
266 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
267 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700268
Dianne Hackborn231cc602009-04-27 17:10:36 -0700269 readAccountInfoLocked();
270 readStatusLocked();
271 readPendingOperationsLocked();
272 readStatisticsLocked();
273 readLegacyAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275
276 public static SyncStorageEngine newTestInstance(Context context) {
277 return new SyncStorageEngine(context);
278 }
279
280 public static void init(Context context) {
281 if (sSyncStorageEngine != null) {
282 throw new IllegalStateException("already initialized");
283 }
284 sSyncStorageEngine = new SyncStorageEngine(context);
285 }
286
287 public static SyncStorageEngine getSingleton() {
288 if (sSyncStorageEngine == null) {
289 throw new IllegalStateException("not initialized");
290 }
291 return sSyncStorageEngine;
292 }
293
Dianne Hackborn231cc602009-04-27 17:10:36 -0700294 @Override public void handleMessage(Message msg) {
295 if (msg.what == MSG_WRITE_STATUS) {
296 synchronized (mAccounts) {
297 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700298 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700299 } else if (msg.what == MSG_WRITE_STATISTICS) {
300 synchronized (mAccounts) {
301 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 }
303 }
304 }
Costin Manolache360e4542009-09-04 13:36:04 -0700305
Dianne Hackborn231cc602009-04-27 17:10:36 -0700306 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
307 synchronized (mAuthorities) {
308 mChangeListeners.register(callback, mask);
309 }
310 }
Costin Manolache360e4542009-09-04 13:36:04 -0700311
Dianne Hackborn231cc602009-04-27 17:10:36 -0700312 public void removeStatusChangeListener(ISyncStatusObserver callback) {
313 synchronized (mAuthorities) {
314 mChangeListeners.unregister(callback);
315 }
316 }
Costin Manolache360e4542009-09-04 13:36:04 -0700317
Dianne Hackborn231cc602009-04-27 17:10:36 -0700318 private void reportChange(int which) {
319 ArrayList<ISyncStatusObserver> reports = null;
320 synchronized (mAuthorities) {
321 int i = mChangeListeners.beginBroadcast();
322 while (i > 0) {
323 i--;
324 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
325 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 continue;
327 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700328 if (reports == null) {
329 reports = new ArrayList<ISyncStatusObserver>(i);
330 }
331 reports.add(mChangeListeners.getBroadcastItem(i));
332 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700333 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700334 }
Costin Manolache360e4542009-09-04 13:36:04 -0700335
Dianne Hackborn231cc602009-04-27 17:10:36 -0700336 if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
Costin Manolache360e4542009-09-04 13:36:04 -0700337
Dianne Hackborn231cc602009-04-27 17:10:36 -0700338 if (reports != null) {
339 int i = reports.size();
340 while (i > 0) {
341 i--;
342 try {
343 reports.get(i).onStatusChanged(which);
344 } catch (RemoteException e) {
345 // The remote callback list will take care of this for us.
346 }
347 }
348 }
349 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700350
Fred Quintanaac9385e2009-06-22 18:00:59 -0700351 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700352 synchronized (mAuthorities) {
353 if (account != null) {
354 AuthorityInfo authority = getAuthorityLocked(account, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700355 "getSyncAutomatically");
356 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700357 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700358
Dianne Hackborn231cc602009-04-27 17:10:36 -0700359 int i = mAuthorities.size();
360 while (i > 0) {
361 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700362 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700363 if (authority.authority.equals(providerName)
364 && authority.enabled) {
365 return true;
366 }
367 }
368 return false;
369 }
370 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371
Fred Quintanaac9385e2009-06-22 18:00:59 -0700372 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700373 boolean wasEnabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700374 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700375 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
376 wasEnabled = authority.enabled;
377 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700378 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700380
381 if (!wasEnabled && sync) {
382 mContext.getContentResolver().requestSync(account, providerName, new Bundle());
383 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700384 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386
Fred Quintana5e787c42009-08-16 23:13:53 -0700387 public int getIsSyncable(Account account, String providerName) {
388 synchronized (mAuthorities) {
389 if (account != null) {
390 AuthorityInfo authority = getAuthorityLocked(account, providerName,
391 "getIsSyncable");
392 if (authority == null) {
393 return -1;
394 }
395 return authority.syncable;
396 }
397
398 int i = mAuthorities.size();
399 while (i > 0) {
400 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700401 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700402 if (authority.authority.equals(providerName)) {
403 return authority.syncable;
404 }
405 }
406 return -1;
407 }
408 }
409
410 public void setIsSyncable(Account account, String providerName, int syncable) {
411 int oldState;
Fred Quintanab763ab22009-08-18 18:07:30 -0700412 if (syncable > 1) {
413 syncable = 1;
414 } else if (syncable < -1) {
415 syncable = -1;
416 }
417 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700418 synchronized (mAuthorities) {
419 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
420 oldState = authority.syncable;
421 authority.syncable = syncable;
422 writeAccountInfoLocked();
423 }
424
425 if (oldState <= 0 && syncable > 0) {
426 mContext.getContentResolver().requestSync(account, providerName, new Bundle());
427 }
428 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
429 }
430
Fred Quintanaac9385e2009-06-22 18:00:59 -0700431 public void setMasterSyncAutomatically(boolean flag) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700432 boolean old;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700433 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700434 old = mMasterSyncAutomatically;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700435 mMasterSyncAutomatically = flag;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700436 writeAccountInfoLocked();
437 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700438 if (!old && flag) {
439 mContext.getContentResolver().requestSync(null, null, new Bundle());
440 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700441 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
442 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700443 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444
Fred Quintanaac9385e2009-06-22 18:00:59 -0700445 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700446 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700447 return mMasterSyncAutomatically;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700448 }
449 }
Costin Manolache360e4542009-09-04 13:36:04 -0700450
Dianne Hackborn7a135592009-05-06 00:28:37 -0700451 public AuthorityInfo getAuthority(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700452 synchronized (mAuthorities) {
453 return getAuthorityLocked(account, authority, null);
454 }
455 }
Costin Manolache360e4542009-09-04 13:36:04 -0700456
Dianne Hackborn231cc602009-04-27 17:10:36 -0700457 public AuthorityInfo getAuthority(int authorityId) {
458 synchronized (mAuthorities) {
459 return mAuthorities.get(authorityId);
460 }
461 }
Costin Manolache360e4542009-09-04 13:36:04 -0700462
Dianne Hackborn231cc602009-04-27 17:10:36 -0700463 /**
464 * Returns true if there is currently a sync operation for the given
465 * account or authority in the pending list, or actively being processed.
466 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700467 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700468 synchronized (mAuthorities) {
469 int i = mPendingOperations.size();
470 while (i > 0) {
471 i--;
472 // TODO(fredq): this probably shouldn't be considering
473 // pending operations.
474 PendingOperation op = mPendingOperations.get(i);
475 if (op.account.equals(account) && op.authority.equals(authority)) {
476 return true;
477 }
478 }
Costin Manolache360e4542009-09-04 13:36:04 -0700479
Dianne Hackborn231cc602009-04-27 17:10:36 -0700480 if (mActiveSync != null) {
481 AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
482 if (ainfo != null && ainfo.account.equals(account)
483 && ainfo.authority.equals(authority)) {
484 return true;
485 }
486 }
487 }
Costin Manolache360e4542009-09-04 13:36:04 -0700488
Dianne Hackborn231cc602009-04-27 17:10:36 -0700489 return false;
490 }
Costin Manolache360e4542009-09-04 13:36:04 -0700491
Dianne Hackborn231cc602009-04-27 17:10:36 -0700492 public PendingOperation insertIntoPending(PendingOperation op) {
493 synchronized (mAuthorities) {
494 if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
495 + " auth=" + op.authority
496 + " src=" + op.syncSource
497 + " extras=" + op.extras);
Costin Manolache360e4542009-09-04 13:36:04 -0700498
Dianne Hackborn231cc602009-04-27 17:10:36 -0700499 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
500 op.authority,
501 -1 /* desired identifier */,
502 true /* write accounts to storage */);
503 if (authority == null) {
504 return null;
505 }
Costin Manolache360e4542009-09-04 13:36:04 -0700506
Dianne Hackborn231cc602009-04-27 17:10:36 -0700507 op = new PendingOperation(op);
508 op.authorityId = authority.ident;
509 mPendingOperations.add(op);
510 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700511
Dianne Hackborn231cc602009-04-27 17:10:36 -0700512 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
513 status.pending = true;
Costin Manolache5ed64cd2009-09-22 14:41:46 -0700514 status.initialize = op.extras != null &&
515 op.extras.containsKey(ContentResolver.SYNC_EXTRAS_INITIALIZE) &&
516 op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700517 }
Costin Manolache360e4542009-09-04 13:36:04 -0700518
Fred Quintanaac9385e2009-06-22 18:00:59 -0700519 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700520 return op;
521 }
522
523 public boolean deleteFromPending(PendingOperation op) {
524 boolean res = false;
525 synchronized (mAuthorities) {
526 if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
527 + " auth=" + op.authority
528 + " src=" + op.syncSource
529 + " extras=" + op.extras);
530 if (mPendingOperations.remove(op)) {
531 if (mPendingOperations.size() == 0
532 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
533 writePendingOperationsLocked();
534 mNumPendingFinished = 0;
535 } else {
536 mNumPendingFinished++;
537 }
Costin Manolache360e4542009-09-04 13:36:04 -0700538
Dianne Hackborn231cc602009-04-27 17:10:36 -0700539 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
540 "deleteFromPending");
541 if (authority != null) {
542 if (DEBUG) Log.v(TAG, "removing - " + authority);
543 final int N = mPendingOperations.size();
544 boolean morePending = false;
545 for (int i=0; i<N; i++) {
546 PendingOperation cur = mPendingOperations.get(i);
547 if (cur.account.equals(op.account)
548 && cur.authority.equals(op.authority)) {
549 morePending = true;
550 break;
551 }
552 }
Costin Manolache360e4542009-09-04 13:36:04 -0700553
Dianne Hackborn231cc602009-04-27 17:10:36 -0700554 if (!morePending) {
555 if (DEBUG) Log.v(TAG, "no more pending!");
556 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
557 status.pending = false;
558 }
559 }
Costin Manolache360e4542009-09-04 13:36:04 -0700560
Dianne Hackborn231cc602009-04-27 17:10:36 -0700561 res = true;
562 }
563 }
Costin Manolache360e4542009-09-04 13:36:04 -0700564
Fred Quintanaac9385e2009-06-22 18:00:59 -0700565 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700566 return res;
567 }
568
569 public int clearPending() {
570 int num;
571 synchronized (mAuthorities) {
572 if (DEBUG) Log.v(TAG, "clearPending");
573 num = mPendingOperations.size();
574 mPendingOperations.clear();
575 final int N = mSyncStatus.size();
576 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700577 mSyncStatus.valueAt(i).pending = false;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700578 }
579 writePendingOperationsLocked();
580 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700581 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700582 return num;
583 }
584
585 /**
586 * Return a copy of the current array of pending operations. The
587 * PendingOperation objects are the real objects stored inside, so that
588 * they can be used with deleteFromPending().
589 */
590 public ArrayList<PendingOperation> getPendingOperations() {
591 synchronized (mAuthorities) {
592 return new ArrayList<PendingOperation>(mPendingOperations);
593 }
594 }
Costin Manolache360e4542009-09-04 13:36:04 -0700595
Dianne Hackborn231cc602009-04-27 17:10:36 -0700596 /**
597 * Return the number of currently pending operations.
598 */
599 public int getPendingOperationCount() {
600 synchronized (mAuthorities) {
601 return mPendingOperations.size();
602 }
603 }
Costin Manolache360e4542009-09-04 13:36:04 -0700604
Dianne Hackborn231cc602009-04-27 17:10:36 -0700605 /**
606 * Called when the set of account has changed, given the new array of
607 * active accounts.
608 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700609 public void doDatabaseCleanup(Account[] accounts) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700610 synchronized (mAuthorities) {
611 if (DEBUG) Log.w(TAG, "Updating for new accounts...");
612 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
613 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
614 while (accIt.hasNext()) {
615 AccountInfo acc = accIt.next();
616 if (!ArrayUtils.contains(accounts, acc.account)) {
617 // This account no longer exists...
618 if (DEBUG) Log.w(TAG, "Account removed: " + acc.account);
619 for (AuthorityInfo auth : acc.authorities.values()) {
620 removing.put(auth.ident, auth);
621 }
622 accIt.remove();
623 }
624 }
Costin Manolache360e4542009-09-04 13:36:04 -0700625
Dianne Hackborn231cc602009-04-27 17:10:36 -0700626 // Clean out all data structures.
627 int i = removing.size();
628 if (i > 0) {
629 while (i > 0) {
630 i--;
631 int ident = removing.keyAt(i);
632 mAuthorities.remove(ident);
633 int j = mSyncStatus.size();
634 while (j > 0) {
635 j--;
636 if (mSyncStatus.keyAt(j) == ident) {
637 mSyncStatus.remove(mSyncStatus.keyAt(j));
638 }
639 }
640 j = mSyncHistory.size();
641 while (j > 0) {
642 j--;
643 if (mSyncHistory.get(j).authorityId == ident) {
644 mSyncHistory.remove(j);
645 }
646 }
647 }
648 writeAccountInfoLocked();
649 writeStatusLocked();
650 writePendingOperationsLocked();
651 writeStatisticsLocked();
652 }
653 }
654 }
655
656 /**
657 * Called when the currently active sync is changing (there can only be
658 * one at a time). Either supply a valid ActiveSyncContext with information
659 * about the sync, or null to stop the currently active sync.
660 */
661 public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
662 synchronized (mAuthorities) {
663 if (activeSyncContext != null) {
664 if (DEBUG) Log.v(TAG, "setActiveSync: account="
665 + activeSyncContext.mSyncOperation.account
666 + " auth=" + activeSyncContext.mSyncOperation.authority
667 + " src=" + activeSyncContext.mSyncOperation.syncSource
668 + " extras=" + activeSyncContext.mSyncOperation.extras);
669 if (mActiveSync != null) {
670 Log.w(TAG, "setActiveSync called with existing active sync!");
671 }
672 AuthorityInfo authority = getAuthorityLocked(
673 activeSyncContext.mSyncOperation.account,
674 activeSyncContext.mSyncOperation.authority,
675 "setActiveSync");
676 if (authority == null) {
677 return;
678 }
679 mActiveSync = new ActiveSyncInfo(authority.ident,
680 authority.account, authority.authority,
681 activeSyncContext.mStartTime);
682 } else {
683 if (DEBUG) Log.v(TAG, "setActiveSync: null");
684 mActiveSync = null;
685 }
686 }
Costin Manolache360e4542009-09-04 13:36:04 -0700687
Fred Quintanaac9385e2009-06-22 18:00:59 -0700688 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700689 }
690
691 /**
692 * To allow others to send active change reports, to poke clients.
693 */
694 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700695 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700696 }
Costin Manolache360e4542009-09-04 13:36:04 -0700697
Dianne Hackborn231cc602009-04-27 17:10:36 -0700698 /**
699 * Note that sync has started for the given account and authority.
700 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700701 public long insertStartSyncEvent(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700702 long now, int source) {
703 long id;
704 synchronized (mAuthorities) {
705 if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
706 + " auth=" + authorityName + " source=" + source);
707 AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
708 "insertStartSyncEvent");
709 if (authority == null) {
710 return -1;
711 }
712 SyncHistoryItem item = new SyncHistoryItem();
713 item.authorityId = authority.ident;
714 item.historyId = mNextHistoryId++;
715 if (mNextHistoryId < 0) mNextHistoryId = 0;
716 item.eventTime = now;
717 item.source = source;
718 item.event = EVENT_START;
719 mSyncHistory.add(0, item);
720 while (mSyncHistory.size() > MAX_HISTORY) {
721 mSyncHistory.remove(mSyncHistory.size()-1);
722 }
723 id = item.historyId;
724 if (DEBUG) Log.v(TAG, "returning historyId " + id);
725 }
Costin Manolache360e4542009-09-04 13:36:04 -0700726
Fred Quintanaac9385e2009-06-22 18:00:59 -0700727 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700728 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 }
730
731 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
732 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700733 synchronized (mAuthorities) {
734 if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
735 SyncHistoryItem item = null;
736 int i = mSyncHistory.size();
737 while (i > 0) {
738 i--;
739 item = mSyncHistory.get(i);
740 if (item.historyId == historyId) {
741 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700743 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 }
Costin Manolache360e4542009-09-04 13:36:04 -0700745
Dianne Hackborn231cc602009-04-27 17:10:36 -0700746 if (item == null) {
747 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
748 return;
749 }
Costin Manolache360e4542009-09-04 13:36:04 -0700750
Dianne Hackborn231cc602009-04-27 17:10:36 -0700751 item.elapsedTime = elapsedTime;
752 item.event = EVENT_STOP;
753 item.mesg = resultMessage;
754 item.downstreamActivity = downstreamActivity;
755 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -0700756
Dianne Hackborn231cc602009-04-27 17:10:36 -0700757 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -0700758
Dianne Hackborn231cc602009-04-27 17:10:36 -0700759 status.numSyncs++;
760 status.totalElapsedTime += elapsedTime;
761 switch (item.source) {
762 case SOURCE_LOCAL:
763 status.numSourceLocal++;
764 break;
765 case SOURCE_POLL:
766 status.numSourcePoll++;
767 break;
768 case SOURCE_USER:
769 status.numSourceUser++;
770 break;
771 case SOURCE_SERVER:
772 status.numSourceServer++;
773 break;
774 }
Costin Manolache360e4542009-09-04 13:36:04 -0700775
Dianne Hackborn231cc602009-04-27 17:10:36 -0700776 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700777 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700778 if (mDayStats[0] == null) {
779 mDayStats[0] = new DayStats(day);
780 } else if (day != mDayStats[0].day) {
781 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
782 mDayStats[0] = new DayStats(day);
783 writeStatisticsNow = true;
784 } else if (mDayStats[0] == null) {
785 }
786 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -0700787
Dianne Hackborn231cc602009-04-27 17:10:36 -0700788 final long lastSyncTime = (item.eventTime + elapsedTime);
789 boolean writeStatusNow = false;
790 if (MESG_SUCCESS.equals(resultMessage)) {
791 // - if successful, update the successful columns
792 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
793 writeStatusNow = true;
794 }
795 status.lastSuccessTime = lastSyncTime;
796 status.lastSuccessSource = item.source;
797 status.lastFailureTime = 0;
798 status.lastFailureSource = -1;
799 status.lastFailureMesg = null;
800 status.initialFailureTime = 0;
801 ds.successCount++;
802 ds.successTime += elapsedTime;
803 } else if (!MESG_CANCELED.equals(resultMessage)) {
804 if (status.lastFailureTime == 0) {
805 writeStatusNow = true;
806 }
807 status.lastFailureTime = lastSyncTime;
808 status.lastFailureSource = item.source;
809 status.lastFailureMesg = resultMessage;
810 if (status.initialFailureTime == 0) {
811 status.initialFailureTime = lastSyncTime;
812 }
813 ds.failureCount++;
814 ds.failureTime += elapsedTime;
815 }
Costin Manolache360e4542009-09-04 13:36:04 -0700816
Dianne Hackborn231cc602009-04-27 17:10:36 -0700817 if (writeStatusNow) {
818 writeStatusLocked();
819 } else if (!hasMessages(MSG_WRITE_STATUS)) {
820 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
821 WRITE_STATUS_DELAY);
822 }
823 if (writeStatisticsNow) {
824 writeStatisticsLocked();
825 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
826 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
827 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -0700828 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700829 }
Costin Manolache360e4542009-09-04 13:36:04 -0700830
Fred Quintanaac9385e2009-06-22 18:00:59 -0700831 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700832 }
833
834 /**
835 * Return the currently active sync information, or null if there is no
836 * active sync. Note that the returned object is the real, live active
837 * sync object, so be careful what you do with it.
838 */
839 public ActiveSyncInfo getActiveSync() {
840 synchronized (mAuthorities) {
841 return mActiveSync;
842 }
843 }
Costin Manolache360e4542009-09-04 13:36:04 -0700844
Dianne Hackborn231cc602009-04-27 17:10:36 -0700845 /**
846 * Return an array of the current sync status for all authorities. Note
847 * that the objects inside the array are the real, live status objects,
848 * so be careful what you do with them.
849 */
850 public ArrayList<SyncStatusInfo> getSyncStatus() {
851 synchronized (mAuthorities) {
852 final int N = mSyncStatus.size();
853 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
854 for (int i=0; i<N; i++) {
855 ops.add(mSyncStatus.valueAt(i));
856 }
857 return ops;
858 }
859 }
Costin Manolache360e4542009-09-04 13:36:04 -0700860
Dianne Hackborn231cc602009-04-27 17:10:36 -0700861 /**
Costin Manolacheb7520982009-09-02 18:03:05 -0700862 * Returns the status that matches the authority and account.
863 *
864 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -0700865 * @param authority the authority whose row should be selected
866 * @return the SyncStatusInfo for the authority, or null if none exists
867 */
Costin Manolacheb7520982009-09-02 18:03:05 -0700868 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
869 if (account == null || authority == null) {
870 throw new IllegalArgumentException();
871 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700872 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700873 final int N = mSyncStatus.size();
874 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700875 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700876 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -0700877
878 if (ainfo != null && ainfo.authority.equals(authority) &&
879 account.equals(ainfo.account)) {
880 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700881 }
882 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700883 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700884 }
885 }
Costin Manolache360e4542009-09-04 13:36:04 -0700886
Dianne Hackborn231cc602009-04-27 17:10:36 -0700887 /**
888 * Return true if the pending status is true of any matching authorities.
889 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700890 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700891 synchronized (mAuthorities) {
892 final int N = mSyncStatus.size();
893 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700894 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700895 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
896 if (ainfo == null) {
897 continue;
898 }
899 if (account != null && !ainfo.account.equals(account)) {
900 continue;
901 }
902 if (ainfo.authority.equals(authority) && cur.pending) {
903 return true;
904 }
905 }
906 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 }
908 }
909
910 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -0700911 * Return an array of the current sync status for all authorities. Note
912 * that the objects inside the array are the real, live status objects,
913 * so be careful what you do with them.
914 */
915 public ArrayList<SyncHistoryItem> getSyncHistory() {
916 synchronized (mAuthorities) {
917 final int N = mSyncHistory.size();
918 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
919 for (int i=0; i<N; i++) {
920 items.add(mSyncHistory.get(i));
921 }
922 return items;
923 }
924 }
Costin Manolache360e4542009-09-04 13:36:04 -0700925
Dianne Hackborn231cc602009-04-27 17:10:36 -0700926 /**
927 * Return an array of the current per-day statistics. Note
928 * that the objects inside the array are the real, live status objects,
929 * so be careful what you do with them.
930 */
931 public DayStats[] getDayStatistics() {
932 synchronized (mAuthorities) {
933 DayStats[] ds = new DayStats[mDayStats.length];
934 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
935 return ds;
936 }
937 }
Costin Manolache360e4542009-09-04 13:36:04 -0700938
Dianne Hackborn231cc602009-04-27 17:10:36 -0700939 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 * If sync is failing for any of the provider/accounts then determine the time at which it
941 * started failing and return the earliest time over all the provider/accounts. If none are
942 * failing then return 0.
943 */
944 public long getInitialSyncFailureTime() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700945 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700946 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700947 return 0;
948 }
Costin Manolache360e4542009-09-04 13:36:04 -0700949
Dianne Hackborn231cc602009-04-27 17:10:36 -0700950 long oldest = 0;
951 int i = mSyncStatus.size();
952 while (i > 0) {
953 i--;
954 SyncStatusInfo stats = mSyncStatus.valueAt(i);
955 AuthorityInfo authority = mAuthorities.get(stats.authorityId);
956 if (authority != null && authority.enabled) {
957 if (oldest == 0 || stats.initialFailureTime < oldest) {
958 oldest = stats.initialFailureTime;
959 }
960 }
961 }
Costin Manolache360e4542009-09-04 13:36:04 -0700962
Dianne Hackborn231cc602009-04-27 17:10:36 -0700963 return oldest;
964 }
965 }
Costin Manolache360e4542009-09-04 13:36:04 -0700966
Dianne Hackborn55280a92009-05-07 15:53:46 -0700967 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700968 mCal.setTimeInMillis(System.currentTimeMillis());
969 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
970 if (mYear != mCal.get(Calendar.YEAR)) {
971 mYear = mCal.get(Calendar.YEAR);
972 mCal.clear();
973 mCal.set(Calendar.YEAR, mYear);
974 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
975 }
976 return dayOfYear + mYearInDays;
977 }
Costin Manolache360e4542009-09-04 13:36:04 -0700978
Dianne Hackborn231cc602009-04-27 17:10:36 -0700979 /**
980 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -0700981 *
Dianne Hackborn231cc602009-04-27 17:10:36 -0700982 * @param accountName The name of the account for the authority.
983 * @param authorityName The name of the authority itself.
984 * @param tag If non-null, this will be used in a log message if the
985 * requested authority does not exist.
986 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700987 private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700988 String tag) {
989 AccountInfo account = mAccounts.get(accountName);
990 if (account == null) {
991 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700992 if (Log.isLoggable(TAG, Log.VERBOSE)) {
993 Log.v(TAG, tag + ": unknown account " + accountName);
994 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700995 }
996 return null;
997 }
998 AuthorityInfo authority = account.authorities.get(authorityName);
999 if (authority == null) {
1000 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001001 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1002 Log.v(TAG, tag + ": unknown authority " + authorityName);
1003 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001004 }
1005 return null;
1006 }
Costin Manolache360e4542009-09-04 13:36:04 -07001007
Dianne Hackborn231cc602009-04-27 17:10:36 -07001008 return authority;
1009 }
Costin Manolache360e4542009-09-04 13:36:04 -07001010
Dianne Hackborn7a135592009-05-06 00:28:37 -07001011 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001012 String authorityName, int ident, boolean doWrite) {
1013 AccountInfo account = mAccounts.get(accountName);
1014 if (account == null) {
1015 account = new AccountInfo(accountName);
1016 mAccounts.put(accountName, account);
1017 }
1018 AuthorityInfo authority = account.authorities.get(authorityName);
1019 if (authority == null) {
1020 if (ident < 0) {
1021 // Look for a new identifier for this authority.
1022 final int N = mAuthorities.size();
1023 ident = 0;
1024 for (int i=0; i<N; i++) {
1025 if (mAuthorities.valueAt(i).ident > ident) {
1026 break;
1027 }
1028 ident++;
1029 }
1030 }
Fred Quintanab763ab22009-08-18 18:07:30 -07001031 Log.d(TAG, "created a new AuthorityInfo for " + accountName
1032 + ", provider " + authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001033 authority = new AuthorityInfo(accountName, authorityName, ident);
1034 account.authorities.put(authorityName, authority);
1035 mAuthorities.put(ident, authority);
1036 if (doWrite) {
1037 writeAccountInfoLocked();
1038 }
1039 }
Costin Manolache360e4542009-09-04 13:36:04 -07001040
Dianne Hackborn231cc602009-04-27 17:10:36 -07001041 return authority;
1042 }
Costin Manolache360e4542009-09-04 13:36:04 -07001043
Dianne Hackborn231cc602009-04-27 17:10:36 -07001044 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1045 SyncStatusInfo status = mSyncStatus.get(authorityId);
1046 if (status == null) {
1047 status = new SyncStatusInfo(authorityId);
1048 mSyncStatus.put(authorityId, status);
1049 }
1050 return status;
1051 }
Costin Manolache360e4542009-09-04 13:36:04 -07001052
Dianne Hackborn55280a92009-05-07 15:53:46 -07001053 public void writeAllState() {
1054 synchronized (mAuthorities) {
1055 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001056
Dianne Hackborn55280a92009-05-07 15:53:46 -07001057 if (mNumPendingFinished > 0) {
1058 // Only write these if they are out of date.
1059 writePendingOperationsLocked();
1060 }
Costin Manolache360e4542009-09-04 13:36:04 -07001061
Dianne Hackborn55280a92009-05-07 15:53:46 -07001062 // Just always write these... they are likely out of date.
1063 writeStatusLocked();
1064 writeStatisticsLocked();
1065 }
1066 }
Costin Manolache360e4542009-09-04 13:36:04 -07001067
Dianne Hackborn231cc602009-04-27 17:10:36 -07001068 /**
1069 * Read all account information back in to the initial engine state.
1070 */
1071 private void readAccountInfoLocked() {
1072 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001074 fis = mAccountInfoFile.openRead();
1075 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1076 XmlPullParser parser = Xml.newPullParser();
1077 parser.setInput(fis, null);
1078 int eventType = parser.getEventType();
1079 while (eventType != XmlPullParser.START_TAG) {
1080 eventType = parser.next();
1081 }
1082 String tagName = parser.getName();
1083 if ("accounts".equals(tagName)) {
1084 String listen = parser.getAttributeValue(
1085 null, "listen-for-tickles");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001086 mMasterSyncAutomatically = listen == null
Dianne Hackborn231cc602009-04-27 17:10:36 -07001087 || Boolean.parseBoolean(listen);
1088 eventType = parser.next();
1089 do {
1090 if (eventType == XmlPullParser.START_TAG
1091 && parser.getDepth() == 2) {
1092 tagName = parser.getName();
1093 if ("authority".equals(tagName)) {
1094 int id = -1;
1095 try {
1096 id = Integer.parseInt(parser.getAttributeValue(
1097 null, "id"));
1098 } catch (NumberFormatException e) {
1099 } catch (NullPointerException e) {
1100 }
1101 if (id >= 0) {
1102 String accountName = parser.getAttributeValue(
1103 null, "account");
Dianne Hackborn7a135592009-05-06 00:28:37 -07001104 String accountType = parser.getAttributeValue(
1105 null, "type");
1106 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001107 accountType = "com.google";
Dianne Hackborn7a135592009-05-06 00:28:37 -07001108 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001109 String authorityName = parser.getAttributeValue(
1110 null, "authority");
1111 String enabled = parser.getAttributeValue(
1112 null, "enabled");
Fred Quintana5e787c42009-08-16 23:13:53 -07001113 String syncable = parser.getAttributeValue(null, "syncable");
1114 AuthorityInfo authority = mAuthorities.get(id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001115 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1116 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001117 + " enabled=" + enabled
1118 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001119 if (authority == null) {
1120 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1121 authority = getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001122 new Account(accountName, accountType),
1123 authorityName, id, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001124 }
1125 if (authority != null) {
1126 authority.enabled = enabled == null
1127 || Boolean.parseBoolean(enabled);
Fred Quintana5e787c42009-08-16 23:13:53 -07001128 if ("unknown".equals(syncable)) {
1129 authority.syncable = -1;
1130 } else {
1131 authority.syncable =
1132 (syncable == null || Boolean.parseBoolean(enabled))
1133 ? 1
1134 : 0;
1135 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001136 } else {
1137 Log.w(TAG, "Failure adding authority: account="
1138 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001139 + " enabled=" + enabled
1140 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001141 }
1142 }
1143 }
1144 }
1145 eventType = parser.next();
1146 } while (eventType != XmlPullParser.END_DOCUMENT);
1147 }
1148 } catch (XmlPullParserException e) {
1149 Log.w(TAG, "Error reading accounts", e);
1150 } catch (java.io.IOException e) {
1151 if (fis == null) Log.i(TAG, "No initial accounts");
1152 else Log.w(TAG, "Error reading accounts", e);
1153 } finally {
1154 if (fis != null) {
1155 try {
1156 fis.close();
1157 } catch (java.io.IOException e1) {
1158 }
1159 }
1160 }
1161 }
Costin Manolache360e4542009-09-04 13:36:04 -07001162
Dianne Hackborn231cc602009-04-27 17:10:36 -07001163 /**
1164 * Write all account information to the account file.
1165 */
1166 private void writeAccountInfoLocked() {
1167 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1168 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001169
Dianne Hackborn231cc602009-04-27 17:10:36 -07001170 try {
1171 fos = mAccountInfoFile.startWrite();
1172 XmlSerializer out = new FastXmlSerializer();
1173 out.setOutput(fos, "utf-8");
1174 out.startDocument(null, true);
1175 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001176
Dianne Hackborn231cc602009-04-27 17:10:36 -07001177 out.startTag(null, "accounts");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001178 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001179 out.attribute(null, "listen-for-tickles", "false");
1180 }
Costin Manolache360e4542009-09-04 13:36:04 -07001181
Dianne Hackborn231cc602009-04-27 17:10:36 -07001182 final int N = mAuthorities.size();
1183 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001184 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001185 out.startTag(null, "authority");
1186 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001187 out.attribute(null, "account", authority.account.name);
1188 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001189 out.attribute(null, "authority", authority.authority);
1190 if (!authority.enabled) {
1191 out.attribute(null, "enabled", "false");
1192 }
Fred Quintana5e787c42009-08-16 23:13:53 -07001193 if (authority.syncable < 0) {
1194 out.attribute(null, "syncable", "unknown");
1195 } else if (authority.syncable == 0) {
1196 out.attribute(null, "syncable", "false");
1197 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001198 out.endTag(null, "authority");
1199 }
Costin Manolache360e4542009-09-04 13:36:04 -07001200
Dianne Hackborn231cc602009-04-27 17:10:36 -07001201 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001202
Dianne Hackborn231cc602009-04-27 17:10:36 -07001203 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001204
Dianne Hackborn231cc602009-04-27 17:10:36 -07001205 mAccountInfoFile.finishWrite(fos);
1206 } catch (java.io.IOException e1) {
1207 Log.w(TAG, "Error writing accounts", e1);
1208 if (fos != null) {
1209 mAccountInfoFile.failWrite(fos);
1210 }
1211 }
1212 }
Costin Manolache360e4542009-09-04 13:36:04 -07001213
Dianne Hackborn231cc602009-04-27 17:10:36 -07001214 static int getIntColumn(Cursor c, String name) {
1215 return c.getInt(c.getColumnIndex(name));
1216 }
Costin Manolache360e4542009-09-04 13:36:04 -07001217
Dianne Hackborn231cc602009-04-27 17:10:36 -07001218 static long getLongColumn(Cursor c, String name) {
1219 return c.getLong(c.getColumnIndex(name));
1220 }
Costin Manolache360e4542009-09-04 13:36:04 -07001221
Dianne Hackborn231cc602009-04-27 17:10:36 -07001222 /**
1223 * Load sync engine state from the old syncmanager database, and then
1224 * erase it. Note that we don't deal with pending operations, active
1225 * sync, or history.
1226 */
1227 private void readLegacyAccountInfoLocked() {
1228 // Look for old database to initialize from.
1229 File file = mContext.getDatabasePath("syncmanager.db");
1230 if (!file.exists()) {
1231 return;
1232 }
1233 String path = file.getPath();
1234 SQLiteDatabase db = null;
1235 try {
1236 db = SQLiteDatabase.openDatabase(path, null,
1237 SQLiteDatabase.OPEN_READONLY);
1238 } catch (SQLiteException e) {
1239 }
Costin Manolache360e4542009-09-04 13:36:04 -07001240
Dianne Hackborn231cc602009-04-27 17:10:36 -07001241 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001242 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001243
Dianne Hackborn231cc602009-04-27 17:10:36 -07001244 // Copy in all of the status information, as well as accounts.
1245 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1246 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1247 qb.setTables("stats, status");
1248 HashMap<String,String> map = new HashMap<String,String>();
1249 map.put("_id", "status._id as _id");
1250 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001251 if (hasType) {
1252 map.put("account_type", "stats.account_type as account_type");
1253 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001254 map.put("authority", "stats.authority as authority");
1255 map.put("totalElapsedTime", "totalElapsedTime");
1256 map.put("numSyncs", "numSyncs");
1257 map.put("numSourceLocal", "numSourceLocal");
1258 map.put("numSourcePoll", "numSourcePoll");
1259 map.put("numSourceServer", "numSourceServer");
1260 map.put("numSourceUser", "numSourceUser");
1261 map.put("lastSuccessSource", "lastSuccessSource");
1262 map.put("lastSuccessTime", "lastSuccessTime");
1263 map.put("lastFailureSource", "lastFailureSource");
1264 map.put("lastFailureTime", "lastFailureTime");
1265 map.put("lastFailureMesg", "lastFailureMesg");
1266 map.put("pending", "pending");
1267 qb.setProjectionMap(map);
1268 qb.appendWhere("stats._id = status.stats_id");
1269 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001271 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001272 String accountType = hasType
1273 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001274 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001275 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001277 String authorityName = c.getString(c.getColumnIndex("authority"));
1278 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001279 new Account(accountName, accountType),
1280 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001281 if (authority != null) {
1282 int i = mSyncStatus.size();
1283 boolean found = false;
1284 SyncStatusInfo st = null;
1285 while (i > 0) {
1286 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001287 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001288 if (st.authorityId == authority.ident) {
1289 found = true;
1290 break;
1291 }
1292 }
1293 if (!found) {
1294 st = new SyncStatusInfo(authority.ident);
1295 mSyncStatus.put(authority.ident, st);
1296 }
1297 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1298 st.numSyncs = getIntColumn(c, "numSyncs");
1299 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1300 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1301 st.numSourceServer = getIntColumn(c, "numSourceServer");
1302 st.numSourceUser = getIntColumn(c, "numSourceUser");
1303 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1304 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1305 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1306 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1307 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1308 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 }
Costin Manolache360e4542009-09-04 13:36:04 -07001311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001313
Dianne Hackborn231cc602009-04-27 17:10:36 -07001314 // Retrieve the settings.
1315 qb = new SQLiteQueryBuilder();
1316 qb.setTables("settings");
1317 c = qb.query(db, null, null, null, null, null, null);
1318 while (c.moveToNext()) {
1319 String name = c.getString(c.getColumnIndex("name"));
1320 String value = c.getString(c.getColumnIndex("value"));
1321 if (name == null) continue;
1322 if (name.equals("listen_for_tickles")) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001323 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001324 } else if (name.startsWith("sync_provider_")) {
1325 String provider = name.substring("sync_provider_".length(),
1326 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001327 int i = mAuthorities.size();
1328 while (i > 0) {
1329 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001330 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001331 if (authority.authority.equals(provider)) {
1332 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001333 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001334 }
1335 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001336 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 }
Costin Manolache360e4542009-09-04 13:36:04 -07001338
Dianne Hackborn231cc602009-04-27 17:10:36 -07001339 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001340
Dianne Hackborn231cc602009-04-27 17:10:36 -07001341 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001342
Dianne Hackborn231cc602009-04-27 17:10:36 -07001343 writeAccountInfoLocked();
1344 writeStatusLocked();
1345 (new File(path)).delete();
1346 }
1347 }
Costin Manolache360e4542009-09-04 13:36:04 -07001348
Dianne Hackborn231cc602009-04-27 17:10:36 -07001349 public static final int STATUS_FILE_END = 0;
1350 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001351
Dianne Hackborn231cc602009-04-27 17:10:36 -07001352 /**
1353 * Read all sync status back in to the initial engine state.
1354 */
1355 private void readStatusLocked() {
1356 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1357 try {
1358 byte[] data = mStatusFile.readFully();
1359 Parcel in = Parcel.obtain();
1360 in.unmarshall(data, 0, data.length);
1361 in.setDataPosition(0);
1362 int token;
1363 while ((token=in.readInt()) != STATUS_FILE_END) {
1364 if (token == STATUS_FILE_ITEM) {
1365 SyncStatusInfo status = new SyncStatusInfo(in);
1366 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1367 status.pending = false;
1368 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1369 + status.authorityId);
1370 mSyncStatus.put(status.authorityId, status);
1371 }
1372 } else {
1373 // Ooops.
1374 Log.w(TAG, "Unknown status token: " + token);
1375 break;
1376 }
1377 }
1378 } catch (java.io.IOException e) {
1379 Log.i(TAG, "No initial status");
1380 }
1381 }
Costin Manolache360e4542009-09-04 13:36:04 -07001382
Dianne Hackborn231cc602009-04-27 17:10:36 -07001383 /**
1384 * Write all sync status to the sync status file.
1385 */
1386 private void writeStatusLocked() {
1387 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001388
Dianne Hackborn231cc602009-04-27 17:10:36 -07001389 // The file is being written, so we don't need to have a scheduled
1390 // write until the next change.
1391 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001392
Dianne Hackborn231cc602009-04-27 17:10:36 -07001393 FileOutputStream fos = null;
1394 try {
1395 fos = mStatusFile.startWrite();
1396 Parcel out = Parcel.obtain();
1397 final int N = mSyncStatus.size();
1398 for (int i=0; i<N; i++) {
1399 SyncStatusInfo status = mSyncStatus.valueAt(i);
1400 out.writeInt(STATUS_FILE_ITEM);
1401 status.writeToParcel(out, 0);
1402 }
1403 out.writeInt(STATUS_FILE_END);
1404 fos.write(out.marshall());
1405 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001406
Dianne Hackborn231cc602009-04-27 17:10:36 -07001407 mStatusFile.finishWrite(fos);
1408 } catch (java.io.IOException e1) {
1409 Log.w(TAG, "Error writing status", e1);
1410 if (fos != null) {
1411 mStatusFile.failWrite(fos);
1412 }
1413 }
1414 }
Costin Manolache360e4542009-09-04 13:36:04 -07001415
Dianne Hackborn231cc602009-04-27 17:10:36 -07001416 public static final int PENDING_OPERATION_VERSION = 1;
Costin Manolache360e4542009-09-04 13:36:04 -07001417
Dianne Hackborn231cc602009-04-27 17:10:36 -07001418 /**
1419 * Read all pending operations back in to the initial engine state.
1420 */
1421 private void readPendingOperationsLocked() {
1422 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1423 try {
1424 byte[] data = mPendingFile.readFully();
1425 Parcel in = Parcel.obtain();
1426 in.unmarshall(data, 0, data.length);
1427 in.setDataPosition(0);
1428 final int SIZE = in.dataSize();
1429 while (in.dataPosition() < SIZE) {
1430 int version = in.readInt();
1431 if (version != PENDING_OPERATION_VERSION) {
1432 Log.w(TAG, "Unknown pending operation version "
1433 + version + "; dropping all ops");
1434 break;
1435 }
1436 int authorityId = in.readInt();
1437 int syncSource = in.readInt();
1438 byte[] flatExtras = in.createByteArray();
1439 AuthorityInfo authority = mAuthorities.get(authorityId);
1440 if (authority != null) {
1441 Bundle extras = null;
1442 if (flatExtras != null) {
1443 extras = unflattenBundle(flatExtras);
1444 }
1445 PendingOperation op = new PendingOperation(
1446 authority.account, syncSource,
1447 authority.authority, extras);
1448 op.authorityId = authorityId;
1449 op.flatExtras = flatExtras;
1450 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
1451 + " auth=" + op.authority
1452 + " src=" + op.syncSource
1453 + " extras=" + op.extras);
1454 mPendingOperations.add(op);
1455 }
1456 }
1457 } catch (java.io.IOException e) {
1458 Log.i(TAG, "No initial pending operations");
1459 }
1460 }
Costin Manolache360e4542009-09-04 13:36:04 -07001461
Dianne Hackborn231cc602009-04-27 17:10:36 -07001462 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
1463 out.writeInt(PENDING_OPERATION_VERSION);
1464 out.writeInt(op.authorityId);
1465 out.writeInt(op.syncSource);
1466 if (op.flatExtras == null && op.extras != null) {
1467 op.flatExtras = flattenBundle(op.extras);
1468 }
1469 out.writeByteArray(op.flatExtras);
1470 }
Costin Manolache360e4542009-09-04 13:36:04 -07001471
Dianne Hackborn231cc602009-04-27 17:10:36 -07001472 /**
1473 * Write all currently pending ops to the pending ops file.
1474 */
1475 private void writePendingOperationsLocked() {
1476 final int N = mPendingOperations.size();
1477 FileOutputStream fos = null;
1478 try {
1479 if (N == 0) {
1480 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
1481 mPendingFile.truncate();
1482 return;
1483 }
Costin Manolache360e4542009-09-04 13:36:04 -07001484
Dianne Hackborn231cc602009-04-27 17:10:36 -07001485 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
1486 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07001487
Dianne Hackborn231cc602009-04-27 17:10:36 -07001488 Parcel out = Parcel.obtain();
1489 for (int i=0; i<N; i++) {
1490 PendingOperation op = mPendingOperations.get(i);
1491 writePendingOperationLocked(op, out);
1492 }
1493 fos.write(out.marshall());
1494 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001495
Dianne Hackborn231cc602009-04-27 17:10:36 -07001496 mPendingFile.finishWrite(fos);
1497 } catch (java.io.IOException e1) {
1498 Log.w(TAG, "Error writing pending operations", e1);
1499 if (fos != null) {
1500 mPendingFile.failWrite(fos);
1501 }
1502 }
1503 }
Costin Manolache360e4542009-09-04 13:36:04 -07001504
Dianne Hackborn231cc602009-04-27 17:10:36 -07001505 /**
1506 * Append the given operation to the pending ops file; if unable to,
1507 * write all pending ops.
1508 */
1509 private void appendPendingOperationLocked(PendingOperation op) {
1510 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
1511 FileOutputStream fos = null;
1512 try {
1513 fos = mPendingFile.openAppend();
1514 } catch (java.io.IOException e) {
1515 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
1516 writePendingOperationsLocked();
1517 return;
1518 }
Costin Manolache360e4542009-09-04 13:36:04 -07001519
Dianne Hackborn231cc602009-04-27 17:10:36 -07001520 try {
1521 Parcel out = Parcel.obtain();
1522 writePendingOperationLocked(op, out);
1523 fos.write(out.marshall());
1524 out.recycle();
1525 } catch (java.io.IOException e1) {
1526 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001528 try {
1529 fos.close();
1530 } catch (java.io.IOException e2) {
1531 }
1532 }
1533 }
Costin Manolache360e4542009-09-04 13:36:04 -07001534
Dianne Hackborn231cc602009-04-27 17:10:36 -07001535 static private byte[] flattenBundle(Bundle bundle) {
1536 byte[] flatData = null;
1537 Parcel parcel = Parcel.obtain();
1538 try {
1539 bundle.writeToParcel(parcel, 0);
1540 flatData = parcel.marshall();
1541 } finally {
1542 parcel.recycle();
1543 }
1544 return flatData;
1545 }
Costin Manolache360e4542009-09-04 13:36:04 -07001546
Dianne Hackborn231cc602009-04-27 17:10:36 -07001547 static private Bundle unflattenBundle(byte[] flatData) {
1548 Bundle bundle;
1549 Parcel parcel = Parcel.obtain();
1550 try {
1551 parcel.unmarshall(flatData, 0, flatData.length);
1552 parcel.setDataPosition(0);
1553 bundle = parcel.readBundle();
1554 } catch (RuntimeException e) {
1555 // A RuntimeException is thrown if we were unable to parse the parcel.
1556 // Create an empty parcel in this case.
1557 bundle = new Bundle();
1558 } finally {
1559 parcel.recycle();
1560 }
1561 return bundle;
1562 }
Costin Manolache360e4542009-09-04 13:36:04 -07001563
Dianne Hackborn231cc602009-04-27 17:10:36 -07001564 public static final int STATISTICS_FILE_END = 0;
1565 public static final int STATISTICS_FILE_ITEM_OLD = 100;
1566 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07001567
Dianne Hackborn231cc602009-04-27 17:10:36 -07001568 /**
1569 * Read all sync statistics back in to the initial engine state.
1570 */
1571 private void readStatisticsLocked() {
1572 try {
1573 byte[] data = mStatisticsFile.readFully();
1574 Parcel in = Parcel.obtain();
1575 in.unmarshall(data, 0, data.length);
1576 in.setDataPosition(0);
1577 int token;
1578 int index = 0;
1579 while ((token=in.readInt()) != STATISTICS_FILE_END) {
1580 if (token == STATISTICS_FILE_ITEM
1581 || token == STATISTICS_FILE_ITEM_OLD) {
1582 int day = in.readInt();
1583 if (token == STATISTICS_FILE_ITEM_OLD) {
1584 day = day - 2009 + 14245; // Magic!
1585 }
1586 DayStats ds = new DayStats(day);
1587 ds.successCount = in.readInt();
1588 ds.successTime = in.readLong();
1589 ds.failureCount = in.readInt();
1590 ds.failureTime = in.readLong();
1591 if (index < mDayStats.length) {
1592 mDayStats[index] = ds;
1593 index++;
1594 }
1595 } else {
1596 // Ooops.
1597 Log.w(TAG, "Unknown stats token: " + token);
1598 break;
1599 }
1600 }
1601 } catch (java.io.IOException e) {
1602 Log.i(TAG, "No initial statistics");
1603 }
1604 }
Costin Manolache360e4542009-09-04 13:36:04 -07001605
Dianne Hackborn231cc602009-04-27 17:10:36 -07001606 /**
1607 * Write all sync statistics to the sync status file.
1608 */
1609 private void writeStatisticsLocked() {
1610 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001611
Dianne Hackborn231cc602009-04-27 17:10:36 -07001612 // The file is being written, so we don't need to have a scheduled
1613 // write until the next change.
1614 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07001615
Dianne Hackborn231cc602009-04-27 17:10:36 -07001616 FileOutputStream fos = null;
1617 try {
1618 fos = mStatisticsFile.startWrite();
1619 Parcel out = Parcel.obtain();
1620 final int N = mDayStats.length;
1621 for (int i=0; i<N; i++) {
1622 DayStats ds = mDayStats[i];
1623 if (ds == null) {
1624 break;
1625 }
1626 out.writeInt(STATISTICS_FILE_ITEM);
1627 out.writeInt(ds.day);
1628 out.writeInt(ds.successCount);
1629 out.writeLong(ds.successTime);
1630 out.writeInt(ds.failureCount);
1631 out.writeLong(ds.failureTime);
1632 }
1633 out.writeInt(STATISTICS_FILE_END);
1634 fos.write(out.marshall());
1635 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001636
Dianne Hackborn231cc602009-04-27 17:10:36 -07001637 mStatisticsFile.finishWrite(fos);
1638 } catch (java.io.IOException e1) {
1639 Log.w(TAG, "Error writing stats", e1);
1640 if (fos != null) {
1641 mStatisticsFile.failWrite(fos);
1642 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 }
1644 }
1645}