blob: f2519840bc49a5ea3e9bcd945c787f6117f075a9 [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 Hackborn231cc602009-04-27 17:10:36 -0700106 public static final int MAX_HISTORY = 15;
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 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700349 // Inform the backup manager about a data change
350 IBackupManager ibm = IBackupManager.Stub.asInterface(
351 ServiceManager.getService(Context.BACKUP_SERVICE));
352 if (ibm != null) {
353 try {
354 ibm.dataChanged("com.android.providers.settings");
355 } catch (RemoteException e) {
356 // Try again later
357 }
358 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700359 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700360
Fred Quintanaac9385e2009-06-22 18:00:59 -0700361 public boolean getSyncAutomatically(Account account, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700362 synchronized (mAuthorities) {
363 if (account != null) {
364 AuthorityInfo authority = getAuthorityLocked(account, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700365 "getSyncAutomatically");
366 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700367 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700368
Dianne Hackborn231cc602009-04-27 17:10:36 -0700369 int i = mAuthorities.size();
370 while (i > 0) {
371 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700372 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700373 if (authority.authority.equals(providerName)
374 && authority.enabled) {
375 return true;
376 }
377 }
378 return false;
379 }
380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381
Fred Quintanaac9385e2009-06-22 18:00:59 -0700382 public void setSyncAutomatically(Account account, String providerName, boolean sync) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700383 boolean wasEnabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700384 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700385 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
386 wasEnabled = authority.enabled;
387 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700388 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700390
391 if (!wasEnabled && sync) {
392 mContext.getContentResolver().requestSync(account, providerName, new Bundle());
393 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700394 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 }
396
Fred Quintana5e787c42009-08-16 23:13:53 -0700397 public int getIsSyncable(Account account, String providerName) {
398 synchronized (mAuthorities) {
399 if (account != null) {
400 AuthorityInfo authority = getAuthorityLocked(account, providerName,
401 "getIsSyncable");
402 if (authority == null) {
403 return -1;
404 }
405 return authority.syncable;
406 }
407
408 int i = mAuthorities.size();
409 while (i > 0) {
410 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700411 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700412 if (authority.authority.equals(providerName)) {
413 return authority.syncable;
414 }
415 }
416 return -1;
417 }
418 }
419
420 public void setIsSyncable(Account account, String providerName, int syncable) {
421 int oldState;
Fred Quintanab763ab22009-08-18 18:07:30 -0700422 if (syncable > 1) {
423 syncable = 1;
424 } else if (syncable < -1) {
425 syncable = -1;
426 }
427 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700428 synchronized (mAuthorities) {
429 AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
430 oldState = authority.syncable;
431 authority.syncable = syncable;
432 writeAccountInfoLocked();
433 }
434
435 if (oldState <= 0 && syncable > 0) {
436 mContext.getContentResolver().requestSync(account, providerName, new Bundle());
437 }
438 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
439 }
440
Fred Quintanaac9385e2009-06-22 18:00:59 -0700441 public void setMasterSyncAutomatically(boolean flag) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700442 boolean old;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700443 synchronized (mAuthorities) {
Joe Onorato8294fad2009-07-15 16:08:44 -0700444 old = mMasterSyncAutomatically;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700445 mMasterSyncAutomatically = flag;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700446 writeAccountInfoLocked();
447 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700448 if (!old && flag) {
449 mContext.getContentResolver().requestSync(null, null, new Bundle());
450 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700451 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
452 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700453 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454
Fred Quintanaac9385e2009-06-22 18:00:59 -0700455 public boolean getMasterSyncAutomatically() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700456 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700457 return mMasterSyncAutomatically;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700458 }
459 }
Costin Manolache360e4542009-09-04 13:36:04 -0700460
Dianne Hackborn7a135592009-05-06 00:28:37 -0700461 public AuthorityInfo getAuthority(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700462 synchronized (mAuthorities) {
463 return getAuthorityLocked(account, authority, null);
464 }
465 }
Costin Manolache360e4542009-09-04 13:36:04 -0700466
Dianne Hackborn231cc602009-04-27 17:10:36 -0700467 public AuthorityInfo getAuthority(int authorityId) {
468 synchronized (mAuthorities) {
469 return mAuthorities.get(authorityId);
470 }
471 }
Costin Manolache360e4542009-09-04 13:36:04 -0700472
Dianne Hackborn231cc602009-04-27 17:10:36 -0700473 /**
474 * Returns true if there is currently a sync operation for the given
475 * account or authority in the pending list, or actively being processed.
476 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700477 public boolean isSyncActive(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700478 synchronized (mAuthorities) {
479 int i = mPendingOperations.size();
480 while (i > 0) {
481 i--;
482 // TODO(fredq): this probably shouldn't be considering
483 // pending operations.
484 PendingOperation op = mPendingOperations.get(i);
485 if (op.account.equals(account) && op.authority.equals(authority)) {
486 return true;
487 }
488 }
Costin Manolache360e4542009-09-04 13:36:04 -0700489
Dianne Hackborn231cc602009-04-27 17:10:36 -0700490 if (mActiveSync != null) {
491 AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
492 if (ainfo != null && ainfo.account.equals(account)
493 && ainfo.authority.equals(authority)) {
494 return true;
495 }
496 }
497 }
Costin Manolache360e4542009-09-04 13:36:04 -0700498
Dianne Hackborn231cc602009-04-27 17:10:36 -0700499 return false;
500 }
Costin Manolache360e4542009-09-04 13:36:04 -0700501
Dianne Hackborn231cc602009-04-27 17:10:36 -0700502 public PendingOperation insertIntoPending(PendingOperation op) {
503 synchronized (mAuthorities) {
504 if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
505 + " auth=" + op.authority
506 + " src=" + op.syncSource
507 + " extras=" + op.extras);
Costin Manolache360e4542009-09-04 13:36:04 -0700508
Dianne Hackborn231cc602009-04-27 17:10:36 -0700509 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
510 op.authority,
511 -1 /* desired identifier */,
512 true /* write accounts to storage */);
513 if (authority == null) {
514 return null;
515 }
Costin Manolache360e4542009-09-04 13:36:04 -0700516
Dianne Hackborn231cc602009-04-27 17:10:36 -0700517 op = new PendingOperation(op);
518 op.authorityId = authority.ident;
519 mPendingOperations.add(op);
520 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700521
Dianne Hackborn231cc602009-04-27 17:10:36 -0700522 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
523 status.pending = true;
524 }
Costin Manolache360e4542009-09-04 13:36:04 -0700525
Fred Quintanaac9385e2009-06-22 18:00:59 -0700526 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700527 return op;
528 }
529
530 public boolean deleteFromPending(PendingOperation op) {
531 boolean res = false;
532 synchronized (mAuthorities) {
533 if (DEBUG) Log.v(TAG, "deleteFromPending: account=" + op.account
534 + " auth=" + op.authority
535 + " src=" + op.syncSource
536 + " extras=" + op.extras);
537 if (mPendingOperations.remove(op)) {
538 if (mPendingOperations.size() == 0
539 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
540 writePendingOperationsLocked();
541 mNumPendingFinished = 0;
542 } else {
543 mNumPendingFinished++;
544 }
Costin Manolache360e4542009-09-04 13:36:04 -0700545
Dianne Hackborn231cc602009-04-27 17:10:36 -0700546 AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
547 "deleteFromPending");
548 if (authority != null) {
549 if (DEBUG) Log.v(TAG, "removing - " + authority);
550 final int N = mPendingOperations.size();
551 boolean morePending = false;
552 for (int i=0; i<N; i++) {
553 PendingOperation cur = mPendingOperations.get(i);
554 if (cur.account.equals(op.account)
555 && cur.authority.equals(op.authority)) {
556 morePending = true;
557 break;
558 }
559 }
Costin Manolache360e4542009-09-04 13:36:04 -0700560
Dianne Hackborn231cc602009-04-27 17:10:36 -0700561 if (!morePending) {
562 if (DEBUG) Log.v(TAG, "no more pending!");
563 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
564 status.pending = false;
565 }
566 }
Costin Manolache360e4542009-09-04 13:36:04 -0700567
Dianne Hackborn231cc602009-04-27 17:10:36 -0700568 res = true;
569 }
570 }
Costin Manolache360e4542009-09-04 13:36:04 -0700571
Fred Quintanaac9385e2009-06-22 18:00:59 -0700572 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700573 return res;
574 }
575
576 public int clearPending() {
577 int num;
578 synchronized (mAuthorities) {
579 if (DEBUG) Log.v(TAG, "clearPending");
580 num = mPendingOperations.size();
581 mPendingOperations.clear();
582 final int N = mSyncStatus.size();
583 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700584 mSyncStatus.valueAt(i).pending = false;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700585 }
586 writePendingOperationsLocked();
587 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700588 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700589 return num;
590 }
591
592 /**
593 * Return a copy of the current array of pending operations. The
594 * PendingOperation objects are the real objects stored inside, so that
595 * they can be used with deleteFromPending().
596 */
597 public ArrayList<PendingOperation> getPendingOperations() {
598 synchronized (mAuthorities) {
599 return new ArrayList<PendingOperation>(mPendingOperations);
600 }
601 }
Costin Manolache360e4542009-09-04 13:36:04 -0700602
Dianne Hackborn231cc602009-04-27 17:10:36 -0700603 /**
604 * Return the number of currently pending operations.
605 */
606 public int getPendingOperationCount() {
607 synchronized (mAuthorities) {
608 return mPendingOperations.size();
609 }
610 }
Costin Manolache360e4542009-09-04 13:36:04 -0700611
Dianne Hackborn231cc602009-04-27 17:10:36 -0700612 /**
613 * Called when the set of account has changed, given the new array of
614 * active accounts.
615 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700616 public void doDatabaseCleanup(Account[] accounts) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700617 synchronized (mAuthorities) {
618 if (DEBUG) Log.w(TAG, "Updating for new accounts...");
619 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
620 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
621 while (accIt.hasNext()) {
622 AccountInfo acc = accIt.next();
623 if (!ArrayUtils.contains(accounts, acc.account)) {
624 // This account no longer exists...
625 if (DEBUG) Log.w(TAG, "Account removed: " + acc.account);
626 for (AuthorityInfo auth : acc.authorities.values()) {
627 removing.put(auth.ident, auth);
628 }
629 accIt.remove();
630 }
631 }
Costin Manolache360e4542009-09-04 13:36:04 -0700632
Dianne Hackborn231cc602009-04-27 17:10:36 -0700633 // Clean out all data structures.
634 int i = removing.size();
635 if (i > 0) {
636 while (i > 0) {
637 i--;
638 int ident = removing.keyAt(i);
639 mAuthorities.remove(ident);
640 int j = mSyncStatus.size();
641 while (j > 0) {
642 j--;
643 if (mSyncStatus.keyAt(j) == ident) {
644 mSyncStatus.remove(mSyncStatus.keyAt(j));
645 }
646 }
647 j = mSyncHistory.size();
648 while (j > 0) {
649 j--;
650 if (mSyncHistory.get(j).authorityId == ident) {
651 mSyncHistory.remove(j);
652 }
653 }
654 }
655 writeAccountInfoLocked();
656 writeStatusLocked();
657 writePendingOperationsLocked();
658 writeStatisticsLocked();
659 }
660 }
661 }
662
663 /**
664 * Called when the currently active sync is changing (there can only be
665 * one at a time). Either supply a valid ActiveSyncContext with information
666 * about the sync, or null to stop the currently active sync.
667 */
668 public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
669 synchronized (mAuthorities) {
670 if (activeSyncContext != null) {
671 if (DEBUG) Log.v(TAG, "setActiveSync: account="
672 + activeSyncContext.mSyncOperation.account
673 + " auth=" + activeSyncContext.mSyncOperation.authority
674 + " src=" + activeSyncContext.mSyncOperation.syncSource
675 + " extras=" + activeSyncContext.mSyncOperation.extras);
676 if (mActiveSync != null) {
677 Log.w(TAG, "setActiveSync called with existing active sync!");
678 }
679 AuthorityInfo authority = getAuthorityLocked(
680 activeSyncContext.mSyncOperation.account,
681 activeSyncContext.mSyncOperation.authority,
682 "setActiveSync");
683 if (authority == null) {
684 return;
685 }
686 mActiveSync = new ActiveSyncInfo(authority.ident,
687 authority.account, authority.authority,
688 activeSyncContext.mStartTime);
689 } else {
690 if (DEBUG) Log.v(TAG, "setActiveSync: null");
691 mActiveSync = null;
692 }
693 }
Costin Manolache360e4542009-09-04 13:36:04 -0700694
Fred Quintanaac9385e2009-06-22 18:00:59 -0700695 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700696 }
697
698 /**
699 * To allow others to send active change reports, to poke clients.
700 */
701 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700702 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700703 }
Costin Manolache360e4542009-09-04 13:36:04 -0700704
Dianne Hackborn231cc602009-04-27 17:10:36 -0700705 /**
706 * Note that sync has started for the given account and authority.
707 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700708 public long insertStartSyncEvent(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700709 long now, int source) {
710 long id;
711 synchronized (mAuthorities) {
712 if (DEBUG) Log.v(TAG, "insertStartSyncEvent: account=" + accountName
713 + " auth=" + authorityName + " source=" + source);
714 AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
715 "insertStartSyncEvent");
716 if (authority == null) {
717 return -1;
718 }
719 SyncHistoryItem item = new SyncHistoryItem();
720 item.authorityId = authority.ident;
721 item.historyId = mNextHistoryId++;
722 if (mNextHistoryId < 0) mNextHistoryId = 0;
723 item.eventTime = now;
724 item.source = source;
725 item.event = EVENT_START;
726 mSyncHistory.add(0, item);
727 while (mSyncHistory.size() > MAX_HISTORY) {
728 mSyncHistory.remove(mSyncHistory.size()-1);
729 }
730 id = item.historyId;
731 if (DEBUG) Log.v(TAG, "returning historyId " + id);
732 }
Costin Manolache360e4542009-09-04 13:36:04 -0700733
Fred Quintanaac9385e2009-06-22 18:00:59 -0700734 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700735 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
737
738 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
739 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700740 synchronized (mAuthorities) {
741 if (DEBUG) Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
742 SyncHistoryItem item = null;
743 int i = mSyncHistory.size();
744 while (i > 0) {
745 i--;
746 item = mSyncHistory.get(i);
747 if (item.historyId == historyId) {
748 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700750 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 }
Costin Manolache360e4542009-09-04 13:36:04 -0700752
Dianne Hackborn231cc602009-04-27 17:10:36 -0700753 if (item == null) {
754 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
755 return;
756 }
Costin Manolache360e4542009-09-04 13:36:04 -0700757
Dianne Hackborn231cc602009-04-27 17:10:36 -0700758 item.elapsedTime = elapsedTime;
759 item.event = EVENT_STOP;
760 item.mesg = resultMessage;
761 item.downstreamActivity = downstreamActivity;
762 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -0700763
Dianne Hackborn231cc602009-04-27 17:10:36 -0700764 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -0700765
Dianne Hackborn231cc602009-04-27 17:10:36 -0700766 status.numSyncs++;
767 status.totalElapsedTime += elapsedTime;
768 switch (item.source) {
769 case SOURCE_LOCAL:
770 status.numSourceLocal++;
771 break;
772 case SOURCE_POLL:
773 status.numSourcePoll++;
774 break;
775 case SOURCE_USER:
776 status.numSourceUser++;
777 break;
778 case SOURCE_SERVER:
779 status.numSourceServer++;
780 break;
781 }
Costin Manolache360e4542009-09-04 13:36:04 -0700782
Dianne Hackborn231cc602009-04-27 17:10:36 -0700783 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700784 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700785 if (mDayStats[0] == null) {
786 mDayStats[0] = new DayStats(day);
787 } else if (day != mDayStats[0].day) {
788 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
789 mDayStats[0] = new DayStats(day);
790 writeStatisticsNow = true;
791 } else if (mDayStats[0] == null) {
792 }
793 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -0700794
Dianne Hackborn231cc602009-04-27 17:10:36 -0700795 final long lastSyncTime = (item.eventTime + elapsedTime);
796 boolean writeStatusNow = false;
797 if (MESG_SUCCESS.equals(resultMessage)) {
798 // - if successful, update the successful columns
799 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
800 writeStatusNow = true;
801 }
802 status.lastSuccessTime = lastSyncTime;
803 status.lastSuccessSource = item.source;
804 status.lastFailureTime = 0;
805 status.lastFailureSource = -1;
806 status.lastFailureMesg = null;
807 status.initialFailureTime = 0;
808 ds.successCount++;
809 ds.successTime += elapsedTime;
810 } else if (!MESG_CANCELED.equals(resultMessage)) {
811 if (status.lastFailureTime == 0) {
812 writeStatusNow = true;
813 }
814 status.lastFailureTime = lastSyncTime;
815 status.lastFailureSource = item.source;
816 status.lastFailureMesg = resultMessage;
817 if (status.initialFailureTime == 0) {
818 status.initialFailureTime = lastSyncTime;
819 }
820 ds.failureCount++;
821 ds.failureTime += elapsedTime;
822 }
Costin Manolache360e4542009-09-04 13:36:04 -0700823
Dianne Hackborn231cc602009-04-27 17:10:36 -0700824 if (writeStatusNow) {
825 writeStatusLocked();
826 } else if (!hasMessages(MSG_WRITE_STATUS)) {
827 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
828 WRITE_STATUS_DELAY);
829 }
830 if (writeStatisticsNow) {
831 writeStatisticsLocked();
832 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
833 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
834 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -0700835 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700836 }
Costin Manolache360e4542009-09-04 13:36:04 -0700837
Fred Quintanaac9385e2009-06-22 18:00:59 -0700838 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700839 }
840
841 /**
842 * Return the currently active sync information, or null if there is no
843 * active sync. Note that the returned object is the real, live active
844 * sync object, so be careful what you do with it.
845 */
846 public ActiveSyncInfo getActiveSync() {
847 synchronized (mAuthorities) {
848 return mActiveSync;
849 }
850 }
Costin Manolache360e4542009-09-04 13:36:04 -0700851
Dianne Hackborn231cc602009-04-27 17:10:36 -0700852 /**
853 * Return an array of the current sync status for all authorities. Note
854 * that the objects inside the array are the real, live status objects,
855 * so be careful what you do with them.
856 */
857 public ArrayList<SyncStatusInfo> getSyncStatus() {
858 synchronized (mAuthorities) {
859 final int N = mSyncStatus.size();
860 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
861 for (int i=0; i<N; i++) {
862 ops.add(mSyncStatus.valueAt(i));
863 }
864 return ops;
865 }
866 }
Costin Manolache360e4542009-09-04 13:36:04 -0700867
Dianne Hackborn231cc602009-04-27 17:10:36 -0700868 /**
Costin Manolacheb7520982009-09-02 18:03:05 -0700869 * Returns the status that matches the authority and account.
870 *
871 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -0700872 * @param authority the authority whose row should be selected
873 * @return the SyncStatusInfo for the authority, or null if none exists
874 */
Costin Manolacheb7520982009-09-02 18:03:05 -0700875 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
876 if (account == null || authority == null) {
877 throw new IllegalArgumentException();
878 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700879 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700880 final int N = mSyncStatus.size();
881 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700882 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700883 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -0700884
885 if (ainfo != null && ainfo.authority.equals(authority) &&
886 account.equals(ainfo.account)) {
887 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700888 }
889 }
Costin Manolacheb7520982009-09-02 18:03:05 -0700890 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700891 }
892 }
Costin Manolache360e4542009-09-04 13:36:04 -0700893
Dianne Hackborn231cc602009-04-27 17:10:36 -0700894 /**
895 * Return true if the pending status is true of any matching authorities.
896 */
Fred Quintanaac9385e2009-06-22 18:00:59 -0700897 public boolean isSyncPending(Account account, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700898 synchronized (mAuthorities) {
899 final int N = mSyncStatus.size();
900 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -0700901 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700902 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
903 if (ainfo == null) {
904 continue;
905 }
906 if (account != null && !ainfo.account.equals(account)) {
907 continue;
908 }
909 if (ainfo.authority.equals(authority) && cur.pending) {
910 return true;
911 }
912 }
913 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915 }
916
917 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -0700918 * Return an array of the current sync status for all authorities. Note
919 * that the objects inside the array are the real, live status objects,
920 * so be careful what you do with them.
921 */
922 public ArrayList<SyncHistoryItem> getSyncHistory() {
923 synchronized (mAuthorities) {
924 final int N = mSyncHistory.size();
925 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
926 for (int i=0; i<N; i++) {
927 items.add(mSyncHistory.get(i));
928 }
929 return items;
930 }
931 }
Costin Manolache360e4542009-09-04 13:36:04 -0700932
Dianne Hackborn231cc602009-04-27 17:10:36 -0700933 /**
934 * Return an array of the current per-day statistics. Note
935 * that the objects inside the array are the real, live status objects,
936 * so be careful what you do with them.
937 */
938 public DayStats[] getDayStatistics() {
939 synchronized (mAuthorities) {
940 DayStats[] ds = new DayStats[mDayStats.length];
941 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
942 return ds;
943 }
944 }
Costin Manolache360e4542009-09-04 13:36:04 -0700945
Dianne Hackborn231cc602009-04-27 17:10:36 -0700946 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 * If sync is failing for any of the provider/accounts then determine the time at which it
948 * started failing and return the earliest time over all the provider/accounts. If none are
949 * failing then return 0.
950 */
951 public long getInitialSyncFailureTime() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700952 synchronized (mAuthorities) {
Fred Quintanaac9385e2009-06-22 18:00:59 -0700953 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700954 return 0;
955 }
Costin Manolache360e4542009-09-04 13:36:04 -0700956
Dianne Hackborn231cc602009-04-27 17:10:36 -0700957 long oldest = 0;
958 int i = mSyncStatus.size();
959 while (i > 0) {
960 i--;
961 SyncStatusInfo stats = mSyncStatus.valueAt(i);
962 AuthorityInfo authority = mAuthorities.get(stats.authorityId);
963 if (authority != null && authority.enabled) {
964 if (oldest == 0 || stats.initialFailureTime < oldest) {
965 oldest = stats.initialFailureTime;
966 }
967 }
968 }
Costin Manolache360e4542009-09-04 13:36:04 -0700969
Dianne Hackborn231cc602009-04-27 17:10:36 -0700970 return oldest;
971 }
972 }
Costin Manolache360e4542009-09-04 13:36:04 -0700973
Dianne Hackborn55280a92009-05-07 15:53:46 -0700974 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700975 mCal.setTimeInMillis(System.currentTimeMillis());
976 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
977 if (mYear != mCal.get(Calendar.YEAR)) {
978 mYear = mCal.get(Calendar.YEAR);
979 mCal.clear();
980 mCal.set(Calendar.YEAR, mYear);
981 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
982 }
983 return dayOfYear + mYearInDays;
984 }
Costin Manolache360e4542009-09-04 13:36:04 -0700985
Dianne Hackborn231cc602009-04-27 17:10:36 -0700986 /**
987 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -0700988 *
Dianne Hackborn231cc602009-04-27 17:10:36 -0700989 * @param accountName The name of the account for the authority.
990 * @param authorityName The name of the authority itself.
991 * @param tag If non-null, this will be used in a log message if the
992 * requested authority does not exist.
993 */
Dianne Hackborn7a135592009-05-06 00:28:37 -0700994 private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700995 String tag) {
996 AccountInfo account = mAccounts.get(accountName);
997 if (account == null) {
998 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700999 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1000 Log.v(TAG, tag + ": unknown account " + accountName);
1001 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001002 }
1003 return null;
1004 }
1005 AuthorityInfo authority = account.authorities.get(authorityName);
1006 if (authority == null) {
1007 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001008 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1009 Log.v(TAG, tag + ": unknown authority " + authorityName);
1010 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001011 }
1012 return null;
1013 }
Costin Manolache360e4542009-09-04 13:36:04 -07001014
Dianne Hackborn231cc602009-04-27 17:10:36 -07001015 return authority;
1016 }
Costin Manolache360e4542009-09-04 13:36:04 -07001017
Dianne Hackborn7a135592009-05-06 00:28:37 -07001018 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001019 String authorityName, int ident, boolean doWrite) {
1020 AccountInfo account = mAccounts.get(accountName);
1021 if (account == null) {
1022 account = new AccountInfo(accountName);
1023 mAccounts.put(accountName, account);
1024 }
1025 AuthorityInfo authority = account.authorities.get(authorityName);
1026 if (authority == null) {
1027 if (ident < 0) {
1028 // Look for a new identifier for this authority.
1029 final int N = mAuthorities.size();
1030 ident = 0;
1031 for (int i=0; i<N; i++) {
1032 if (mAuthorities.valueAt(i).ident > ident) {
1033 break;
1034 }
1035 ident++;
1036 }
1037 }
Fred Quintanab763ab22009-08-18 18:07:30 -07001038 Log.d(TAG, "created a new AuthorityInfo for " + accountName
1039 + ", provider " + authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001040 authority = new AuthorityInfo(accountName, authorityName, ident);
1041 account.authorities.put(authorityName, authority);
1042 mAuthorities.put(ident, authority);
1043 if (doWrite) {
1044 writeAccountInfoLocked();
1045 }
1046 }
Costin Manolache360e4542009-09-04 13:36:04 -07001047
Dianne Hackborn231cc602009-04-27 17:10:36 -07001048 return authority;
1049 }
Costin Manolache360e4542009-09-04 13:36:04 -07001050
Dianne Hackborn231cc602009-04-27 17:10:36 -07001051 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1052 SyncStatusInfo status = mSyncStatus.get(authorityId);
1053 if (status == null) {
1054 status = new SyncStatusInfo(authorityId);
1055 mSyncStatus.put(authorityId, status);
1056 }
1057 return status;
1058 }
Costin Manolache360e4542009-09-04 13:36:04 -07001059
Dianne Hackborn55280a92009-05-07 15:53:46 -07001060 public void writeAllState() {
1061 synchronized (mAuthorities) {
1062 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001063
Dianne Hackborn55280a92009-05-07 15:53:46 -07001064 if (mNumPendingFinished > 0) {
1065 // Only write these if they are out of date.
1066 writePendingOperationsLocked();
1067 }
Costin Manolache360e4542009-09-04 13:36:04 -07001068
Dianne Hackborn55280a92009-05-07 15:53:46 -07001069 // Just always write these... they are likely out of date.
1070 writeStatusLocked();
1071 writeStatisticsLocked();
1072 }
1073 }
Costin Manolache360e4542009-09-04 13:36:04 -07001074
Dianne Hackborn231cc602009-04-27 17:10:36 -07001075 /**
1076 * Read all account information back in to the initial engine state.
1077 */
1078 private void readAccountInfoLocked() {
1079 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001081 fis = mAccountInfoFile.openRead();
1082 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1083 XmlPullParser parser = Xml.newPullParser();
1084 parser.setInput(fis, null);
1085 int eventType = parser.getEventType();
1086 while (eventType != XmlPullParser.START_TAG) {
1087 eventType = parser.next();
1088 }
1089 String tagName = parser.getName();
1090 if ("accounts".equals(tagName)) {
1091 String listen = parser.getAttributeValue(
1092 null, "listen-for-tickles");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001093 mMasterSyncAutomatically = listen == null
Dianne Hackborn231cc602009-04-27 17:10:36 -07001094 || Boolean.parseBoolean(listen);
1095 eventType = parser.next();
1096 do {
1097 if (eventType == XmlPullParser.START_TAG
1098 && parser.getDepth() == 2) {
1099 tagName = parser.getName();
1100 if ("authority".equals(tagName)) {
1101 int id = -1;
1102 try {
1103 id = Integer.parseInt(parser.getAttributeValue(
1104 null, "id"));
1105 } catch (NumberFormatException e) {
1106 } catch (NullPointerException e) {
1107 }
1108 if (id >= 0) {
1109 String accountName = parser.getAttributeValue(
1110 null, "account");
Dianne Hackborn7a135592009-05-06 00:28:37 -07001111 String accountType = parser.getAttributeValue(
1112 null, "type");
1113 if (accountType == null) {
1114 accountType = "com.google.GAIA";
1115 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001116 String authorityName = parser.getAttributeValue(
1117 null, "authority");
1118 String enabled = parser.getAttributeValue(
1119 null, "enabled");
Fred Quintana5e787c42009-08-16 23:13:53 -07001120 String syncable = parser.getAttributeValue(null, "syncable");
1121 AuthorityInfo authority = mAuthorities.get(id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001122 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1123 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001124 + " enabled=" + enabled
1125 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001126 if (authority == null) {
1127 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1128 authority = getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001129 new Account(accountName, accountType),
1130 authorityName, id, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001131 }
1132 if (authority != null) {
1133 authority.enabled = enabled == null
1134 || Boolean.parseBoolean(enabled);
Fred Quintana5e787c42009-08-16 23:13:53 -07001135 if ("unknown".equals(syncable)) {
1136 authority.syncable = -1;
1137 } else {
1138 authority.syncable =
1139 (syncable == null || Boolean.parseBoolean(enabled))
1140 ? 1
1141 : 0;
1142 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001143 } else {
1144 Log.w(TAG, "Failure adding authority: account="
1145 + accountName + " auth=" + authorityName
Fred Quintana5e787c42009-08-16 23:13:53 -07001146 + " enabled=" + enabled
1147 + " syncable=" + syncable);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001148 }
1149 }
1150 }
1151 }
1152 eventType = parser.next();
1153 } while (eventType != XmlPullParser.END_DOCUMENT);
1154 }
1155 } catch (XmlPullParserException e) {
1156 Log.w(TAG, "Error reading accounts", e);
1157 } catch (java.io.IOException e) {
1158 if (fis == null) Log.i(TAG, "No initial accounts");
1159 else Log.w(TAG, "Error reading accounts", e);
1160 } finally {
1161 if (fis != null) {
1162 try {
1163 fis.close();
1164 } catch (java.io.IOException e1) {
1165 }
1166 }
1167 }
1168 }
Costin Manolache360e4542009-09-04 13:36:04 -07001169
Dianne Hackborn231cc602009-04-27 17:10:36 -07001170 /**
1171 * Write all account information to the account file.
1172 */
1173 private void writeAccountInfoLocked() {
1174 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1175 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001176
Dianne Hackborn231cc602009-04-27 17:10:36 -07001177 try {
1178 fos = mAccountInfoFile.startWrite();
1179 XmlSerializer out = new FastXmlSerializer();
1180 out.setOutput(fos, "utf-8");
1181 out.startDocument(null, true);
1182 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001183
Dianne Hackborn231cc602009-04-27 17:10:36 -07001184 out.startTag(null, "accounts");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001185 if (!mMasterSyncAutomatically) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001186 out.attribute(null, "listen-for-tickles", "false");
1187 }
Costin Manolache360e4542009-09-04 13:36:04 -07001188
Dianne Hackborn231cc602009-04-27 17:10:36 -07001189 final int N = mAuthorities.size();
1190 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001191 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001192 out.startTag(null, "authority");
1193 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001194 out.attribute(null, "account", authority.account.name);
1195 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001196 out.attribute(null, "authority", authority.authority);
1197 if (!authority.enabled) {
1198 out.attribute(null, "enabled", "false");
1199 }
Fred Quintana5e787c42009-08-16 23:13:53 -07001200 if (authority.syncable < 0) {
1201 out.attribute(null, "syncable", "unknown");
1202 } else if (authority.syncable == 0) {
1203 out.attribute(null, "syncable", "false");
1204 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001205 out.endTag(null, "authority");
1206 }
Costin Manolache360e4542009-09-04 13:36:04 -07001207
Dianne Hackborn231cc602009-04-27 17:10:36 -07001208 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001209
Dianne Hackborn231cc602009-04-27 17:10:36 -07001210 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001211
Dianne Hackborn231cc602009-04-27 17:10:36 -07001212 mAccountInfoFile.finishWrite(fos);
1213 } catch (java.io.IOException e1) {
1214 Log.w(TAG, "Error writing accounts", e1);
1215 if (fos != null) {
1216 mAccountInfoFile.failWrite(fos);
1217 }
1218 }
1219 }
Costin Manolache360e4542009-09-04 13:36:04 -07001220
Dianne Hackborn231cc602009-04-27 17:10:36 -07001221 static int getIntColumn(Cursor c, String name) {
1222 return c.getInt(c.getColumnIndex(name));
1223 }
Costin Manolache360e4542009-09-04 13:36:04 -07001224
Dianne Hackborn231cc602009-04-27 17:10:36 -07001225 static long getLongColumn(Cursor c, String name) {
1226 return c.getLong(c.getColumnIndex(name));
1227 }
Costin Manolache360e4542009-09-04 13:36:04 -07001228
Dianne Hackborn231cc602009-04-27 17:10:36 -07001229 /**
1230 * Load sync engine state from the old syncmanager database, and then
1231 * erase it. Note that we don't deal with pending operations, active
1232 * sync, or history.
1233 */
1234 private void readLegacyAccountInfoLocked() {
1235 // Look for old database to initialize from.
1236 File file = mContext.getDatabasePath("syncmanager.db");
1237 if (!file.exists()) {
1238 return;
1239 }
1240 String path = file.getPath();
1241 SQLiteDatabase db = null;
1242 try {
1243 db = SQLiteDatabase.openDatabase(path, null,
1244 SQLiteDatabase.OPEN_READONLY);
1245 } catch (SQLiteException e) {
1246 }
Costin Manolache360e4542009-09-04 13:36:04 -07001247
Dianne Hackborn231cc602009-04-27 17:10:36 -07001248 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001249 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001250
Dianne Hackborn231cc602009-04-27 17:10:36 -07001251 // Copy in all of the status information, as well as accounts.
1252 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1253 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1254 qb.setTables("stats, status");
1255 HashMap<String,String> map = new HashMap<String,String>();
1256 map.put("_id", "status._id as _id");
1257 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001258 if (hasType) {
1259 map.put("account_type", "stats.account_type as account_type");
1260 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001261 map.put("authority", "stats.authority as authority");
1262 map.put("totalElapsedTime", "totalElapsedTime");
1263 map.put("numSyncs", "numSyncs");
1264 map.put("numSourceLocal", "numSourceLocal");
1265 map.put("numSourcePoll", "numSourcePoll");
1266 map.put("numSourceServer", "numSourceServer");
1267 map.put("numSourceUser", "numSourceUser");
1268 map.put("lastSuccessSource", "lastSuccessSource");
1269 map.put("lastSuccessTime", "lastSuccessTime");
1270 map.put("lastFailureSource", "lastFailureSource");
1271 map.put("lastFailureTime", "lastFailureTime");
1272 map.put("lastFailureMesg", "lastFailureMesg");
1273 map.put("pending", "pending");
1274 qb.setProjectionMap(map);
1275 qb.appendWhere("stats._id = status.stats_id");
1276 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001278 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001279 String accountType = hasType
1280 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001281 if (accountType == null) {
1282 accountType = "com.google.GAIA";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001284 String authorityName = c.getString(c.getColumnIndex("authority"));
1285 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Dianne Hackborn7a135592009-05-06 00:28:37 -07001286 new Account(accountName, accountType),
1287 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001288 if (authority != null) {
1289 int i = mSyncStatus.size();
1290 boolean found = false;
1291 SyncStatusInfo st = null;
1292 while (i > 0) {
1293 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001294 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001295 if (st.authorityId == authority.ident) {
1296 found = true;
1297 break;
1298 }
1299 }
1300 if (!found) {
1301 st = new SyncStatusInfo(authority.ident);
1302 mSyncStatus.put(authority.ident, st);
1303 }
1304 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1305 st.numSyncs = getIntColumn(c, "numSyncs");
1306 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1307 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1308 st.numSourceServer = getIntColumn(c, "numSourceServer");
1309 st.numSourceUser = getIntColumn(c, "numSourceUser");
1310 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1311 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1312 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1313 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1314 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1315 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 }
Costin Manolache360e4542009-09-04 13:36:04 -07001318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001320
Dianne Hackborn231cc602009-04-27 17:10:36 -07001321 // Retrieve the settings.
1322 qb = new SQLiteQueryBuilder();
1323 qb.setTables("settings");
1324 c = qb.query(db, null, null, null, null, null, null);
1325 while (c.moveToNext()) {
1326 String name = c.getString(c.getColumnIndex("name"));
1327 String value = c.getString(c.getColumnIndex("value"));
1328 if (name == null) continue;
1329 if (name.equals("listen_for_tickles")) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001330 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001331 } else if (name.startsWith("sync_provider_")) {
1332 String provider = name.substring("sync_provider_".length(),
1333 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001334 int i = mAuthorities.size();
1335 while (i > 0) {
1336 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001337 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001338 if (authority.authority.equals(provider)) {
1339 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001340 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001341 }
1342 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 }
Costin Manolache360e4542009-09-04 13:36:04 -07001345
Dianne Hackborn231cc602009-04-27 17:10:36 -07001346 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001347
Dianne Hackborn231cc602009-04-27 17:10:36 -07001348 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001349
Dianne Hackborn231cc602009-04-27 17:10:36 -07001350 writeAccountInfoLocked();
1351 writeStatusLocked();
1352 (new File(path)).delete();
1353 }
1354 }
Costin Manolache360e4542009-09-04 13:36:04 -07001355
Dianne Hackborn231cc602009-04-27 17:10:36 -07001356 public static final int STATUS_FILE_END = 0;
1357 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001358
Dianne Hackborn231cc602009-04-27 17:10:36 -07001359 /**
1360 * Read all sync status back in to the initial engine state.
1361 */
1362 private void readStatusLocked() {
1363 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1364 try {
1365 byte[] data = mStatusFile.readFully();
1366 Parcel in = Parcel.obtain();
1367 in.unmarshall(data, 0, data.length);
1368 in.setDataPosition(0);
1369 int token;
1370 while ((token=in.readInt()) != STATUS_FILE_END) {
1371 if (token == STATUS_FILE_ITEM) {
1372 SyncStatusInfo status = new SyncStatusInfo(in);
1373 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1374 status.pending = false;
1375 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1376 + status.authorityId);
1377 mSyncStatus.put(status.authorityId, status);
1378 }
1379 } else {
1380 // Ooops.
1381 Log.w(TAG, "Unknown status token: " + token);
1382 break;
1383 }
1384 }
1385 } catch (java.io.IOException e) {
1386 Log.i(TAG, "No initial status");
1387 }
1388 }
Costin Manolache360e4542009-09-04 13:36:04 -07001389
Dianne Hackborn231cc602009-04-27 17:10:36 -07001390 /**
1391 * Write all sync status to the sync status file.
1392 */
1393 private void writeStatusLocked() {
1394 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001395
Dianne Hackborn231cc602009-04-27 17:10:36 -07001396 // The file is being written, so we don't need to have a scheduled
1397 // write until the next change.
1398 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001399
Dianne Hackborn231cc602009-04-27 17:10:36 -07001400 FileOutputStream fos = null;
1401 try {
1402 fos = mStatusFile.startWrite();
1403 Parcel out = Parcel.obtain();
1404 final int N = mSyncStatus.size();
1405 for (int i=0; i<N; i++) {
1406 SyncStatusInfo status = mSyncStatus.valueAt(i);
1407 out.writeInt(STATUS_FILE_ITEM);
1408 status.writeToParcel(out, 0);
1409 }
1410 out.writeInt(STATUS_FILE_END);
1411 fos.write(out.marshall());
1412 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001413
Dianne Hackborn231cc602009-04-27 17:10:36 -07001414 mStatusFile.finishWrite(fos);
1415 } catch (java.io.IOException e1) {
1416 Log.w(TAG, "Error writing status", e1);
1417 if (fos != null) {
1418 mStatusFile.failWrite(fos);
1419 }
1420 }
1421 }
Costin Manolache360e4542009-09-04 13:36:04 -07001422
Dianne Hackborn231cc602009-04-27 17:10:36 -07001423 public static final int PENDING_OPERATION_VERSION = 1;
Costin Manolache360e4542009-09-04 13:36:04 -07001424
Dianne Hackborn231cc602009-04-27 17:10:36 -07001425 /**
1426 * Read all pending operations back in to the initial engine state.
1427 */
1428 private void readPendingOperationsLocked() {
1429 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
1430 try {
1431 byte[] data = mPendingFile.readFully();
1432 Parcel in = Parcel.obtain();
1433 in.unmarshall(data, 0, data.length);
1434 in.setDataPosition(0);
1435 final int SIZE = in.dataSize();
1436 while (in.dataPosition() < SIZE) {
1437 int version = in.readInt();
1438 if (version != PENDING_OPERATION_VERSION) {
1439 Log.w(TAG, "Unknown pending operation version "
1440 + version + "; dropping all ops");
1441 break;
1442 }
1443 int authorityId = in.readInt();
1444 int syncSource = in.readInt();
1445 byte[] flatExtras = in.createByteArray();
1446 AuthorityInfo authority = mAuthorities.get(authorityId);
1447 if (authority != null) {
1448 Bundle extras = null;
1449 if (flatExtras != null) {
1450 extras = unflattenBundle(flatExtras);
1451 }
1452 PendingOperation op = new PendingOperation(
1453 authority.account, syncSource,
1454 authority.authority, extras);
1455 op.authorityId = authorityId;
1456 op.flatExtras = flatExtras;
1457 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
1458 + " auth=" + op.authority
1459 + " src=" + op.syncSource
1460 + " extras=" + op.extras);
1461 mPendingOperations.add(op);
1462 }
1463 }
1464 } catch (java.io.IOException e) {
1465 Log.i(TAG, "No initial pending operations");
1466 }
1467 }
Costin Manolache360e4542009-09-04 13:36:04 -07001468
Dianne Hackborn231cc602009-04-27 17:10:36 -07001469 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
1470 out.writeInt(PENDING_OPERATION_VERSION);
1471 out.writeInt(op.authorityId);
1472 out.writeInt(op.syncSource);
1473 if (op.flatExtras == null && op.extras != null) {
1474 op.flatExtras = flattenBundle(op.extras);
1475 }
1476 out.writeByteArray(op.flatExtras);
1477 }
Costin Manolache360e4542009-09-04 13:36:04 -07001478
Dianne Hackborn231cc602009-04-27 17:10:36 -07001479 /**
1480 * Write all currently pending ops to the pending ops file.
1481 */
1482 private void writePendingOperationsLocked() {
1483 final int N = mPendingOperations.size();
1484 FileOutputStream fos = null;
1485 try {
1486 if (N == 0) {
1487 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
1488 mPendingFile.truncate();
1489 return;
1490 }
Costin Manolache360e4542009-09-04 13:36:04 -07001491
Dianne Hackborn231cc602009-04-27 17:10:36 -07001492 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
1493 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07001494
Dianne Hackborn231cc602009-04-27 17:10:36 -07001495 Parcel out = Parcel.obtain();
1496 for (int i=0; i<N; i++) {
1497 PendingOperation op = mPendingOperations.get(i);
1498 writePendingOperationLocked(op, out);
1499 }
1500 fos.write(out.marshall());
1501 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001502
Dianne Hackborn231cc602009-04-27 17:10:36 -07001503 mPendingFile.finishWrite(fos);
1504 } catch (java.io.IOException e1) {
1505 Log.w(TAG, "Error writing pending operations", e1);
1506 if (fos != null) {
1507 mPendingFile.failWrite(fos);
1508 }
1509 }
1510 }
Costin Manolache360e4542009-09-04 13:36:04 -07001511
Dianne Hackborn231cc602009-04-27 17:10:36 -07001512 /**
1513 * Append the given operation to the pending ops file; if unable to,
1514 * write all pending ops.
1515 */
1516 private void appendPendingOperationLocked(PendingOperation op) {
1517 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
1518 FileOutputStream fos = null;
1519 try {
1520 fos = mPendingFile.openAppend();
1521 } catch (java.io.IOException e) {
1522 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
1523 writePendingOperationsLocked();
1524 return;
1525 }
Costin Manolache360e4542009-09-04 13:36:04 -07001526
Dianne Hackborn231cc602009-04-27 17:10:36 -07001527 try {
1528 Parcel out = Parcel.obtain();
1529 writePendingOperationLocked(op, out);
1530 fos.write(out.marshall());
1531 out.recycle();
1532 } catch (java.io.IOException e1) {
1533 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001535 try {
1536 fos.close();
1537 } catch (java.io.IOException e2) {
1538 }
1539 }
1540 }
Costin Manolache360e4542009-09-04 13:36:04 -07001541
Dianne Hackborn231cc602009-04-27 17:10:36 -07001542 static private byte[] flattenBundle(Bundle bundle) {
1543 byte[] flatData = null;
1544 Parcel parcel = Parcel.obtain();
1545 try {
1546 bundle.writeToParcel(parcel, 0);
1547 flatData = parcel.marshall();
1548 } finally {
1549 parcel.recycle();
1550 }
1551 return flatData;
1552 }
Costin Manolache360e4542009-09-04 13:36:04 -07001553
Dianne Hackborn231cc602009-04-27 17:10:36 -07001554 static private Bundle unflattenBundle(byte[] flatData) {
1555 Bundle bundle;
1556 Parcel parcel = Parcel.obtain();
1557 try {
1558 parcel.unmarshall(flatData, 0, flatData.length);
1559 parcel.setDataPosition(0);
1560 bundle = parcel.readBundle();
1561 } catch (RuntimeException e) {
1562 // A RuntimeException is thrown if we were unable to parse the parcel.
1563 // Create an empty parcel in this case.
1564 bundle = new Bundle();
1565 } finally {
1566 parcel.recycle();
1567 }
1568 return bundle;
1569 }
Costin Manolache360e4542009-09-04 13:36:04 -07001570
Dianne Hackborn231cc602009-04-27 17:10:36 -07001571 public static final int STATISTICS_FILE_END = 0;
1572 public static final int STATISTICS_FILE_ITEM_OLD = 100;
1573 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07001574
Dianne Hackborn231cc602009-04-27 17:10:36 -07001575 /**
1576 * Read all sync statistics back in to the initial engine state.
1577 */
1578 private void readStatisticsLocked() {
1579 try {
1580 byte[] data = mStatisticsFile.readFully();
1581 Parcel in = Parcel.obtain();
1582 in.unmarshall(data, 0, data.length);
1583 in.setDataPosition(0);
1584 int token;
1585 int index = 0;
1586 while ((token=in.readInt()) != STATISTICS_FILE_END) {
1587 if (token == STATISTICS_FILE_ITEM
1588 || token == STATISTICS_FILE_ITEM_OLD) {
1589 int day = in.readInt();
1590 if (token == STATISTICS_FILE_ITEM_OLD) {
1591 day = day - 2009 + 14245; // Magic!
1592 }
1593 DayStats ds = new DayStats(day);
1594 ds.successCount = in.readInt();
1595 ds.successTime = in.readLong();
1596 ds.failureCount = in.readInt();
1597 ds.failureTime = in.readLong();
1598 if (index < mDayStats.length) {
1599 mDayStats[index] = ds;
1600 index++;
1601 }
1602 } else {
1603 // Ooops.
1604 Log.w(TAG, "Unknown stats token: " + token);
1605 break;
1606 }
1607 }
1608 } catch (java.io.IOException e) {
1609 Log.i(TAG, "No initial statistics");
1610 }
1611 }
Costin Manolache360e4542009-09-04 13:36:04 -07001612
Dianne Hackborn231cc602009-04-27 17:10:36 -07001613 /**
1614 * Write all sync statistics to the sync status file.
1615 */
1616 private void writeStatisticsLocked() {
1617 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001618
Dianne Hackborn231cc602009-04-27 17:10:36 -07001619 // The file is being written, so we don't need to have a scheduled
1620 // write until the next change.
1621 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07001622
Dianne Hackborn231cc602009-04-27 17:10:36 -07001623 FileOutputStream fos = null;
1624 try {
1625 fos = mStatisticsFile.startWrite();
1626 Parcel out = Parcel.obtain();
1627 final int N = mDayStats.length;
1628 for (int i=0; i<N; i++) {
1629 DayStats ds = mDayStats[i];
1630 if (ds == null) {
1631 break;
1632 }
1633 out.writeInt(STATISTICS_FILE_ITEM);
1634 out.writeInt(ds.day);
1635 out.writeInt(ds.successCount);
1636 out.writeLong(ds.successTime);
1637 out.writeInt(ds.failureCount);
1638 out.writeLong(ds.failureTime);
1639 }
1640 out.writeInt(STATISTICS_FILE_END);
1641 fos.write(out.marshall());
1642 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001643
Dianne Hackborn231cc602009-04-27 17:10:36 -07001644 mStatisticsFile.finishWrite(fos);
1645 } catch (java.io.IOException e1) {
1646 Log.w(TAG, "Error writing stats", e1);
1647 if (fos != null) {
1648 mStatisticsFile.failWrite(fos);
1649 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 }
1651 }
1652}