blob: 6c7e9404901a4b6e31fc649675f5c54c0dc470fc [file] [log] [blame]
Dianne Hackborn231cc602009-04-27 17:10:36 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017package android.content;
18
Dianne Hackborn231cc602009-04-27 17:10:36 -070019import com.android.internal.os.AtomicFile;
20import com.android.internal.util.ArrayUtils;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080021import com.android.internal.util.FastXmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022
Jason parksa3cdaa52011-01-13 14:15:43 -060023import 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 Yamasanif29f2362012-04-05 18:29:52 -070028import android.accounts.AccountAndUser;
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;
Ashish Sharma69d95de2012-04-11 17:27:24 -070040import android.os.SystemClock;
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;
Jason parksa3cdaa52011-01-13 14:15:43 -060044import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
Dianne Hackborn231cc602009-04-27 17:10:36 -070046import java.io.File;
47import java.io.FileInputStream;
48import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.ArrayList;
Dianne Hackborn231cc602009-04-27 17:10:36 -070050import java.util.Calendar;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070052import java.util.Iterator;
Ashish Sharma69d95de2012-04-11 17:27:24 -070053import java.util.Random;
Jason parks1125d782011-01-12 09:47:26 -060054import java.util.TimeZone;
Jason parksa3cdaa52011-01-13 14:15:43 -060055import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57/**
Dianne Hackborn231cc602009-04-27 17:10:36 -070058 * Singleton that tracks the sync data and overall sync
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 * history on the device.
Costin Manolache360e4542009-09-04 13:36:04 -070060 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 * @hide
62 */
Dianne Hackborn231cc602009-04-27 17:10:36 -070063public class SyncStorageEngine extends Handler {
Amith Yamasani04e0d262012-02-14 11:50:53 -080064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 private static final String TAG = "SyncManager";
Dianne Hackborn231cc602009-04-27 17:10:36 -070066 private static final boolean DEBUG_FILE = false;
Costin Manolache360e4542009-09-04 13:36:04 -070067
Amith Yamasani04e0d262012-02-14 11:50:53 -080068 private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId";
69 private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles";
Ashish Sharma69d95de2012-04-11 17:27:24 -070070 private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds";
Amith Yamasani04e0d262012-02-14 11:50:53 -080071 private static final String XML_ATTR_ENABLED = "enabled";
72 private static final String XML_ATTR_USER = "user";
73 private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
74
Fred Quintanac5d1c6d2010-01-27 12:17:49 -080075 private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
76
Dianne Hackborn231cc602009-04-27 17:10:36 -070077 // @VisibleForTesting
78 static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Dianne Hackborn231cc602009-04-27 17:10:36 -070080 /** Enum value for a sync start event. */
81 public static final int EVENT_START = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Dianne Hackborn231cc602009-04-27 17:10:36 -070083 /** Enum value for a sync stop event. */
84 public static final int EVENT_STOP = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085
Dianne Hackborn231cc602009-04-27 17:10:36 -070086 // TODO: i18n -- grab these out of resources.
87 /** String names for the sync event types. */
88 public static final String[] EVENTS = { "START", "STOP" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
Dianne Hackborn231cc602009-04-27 17:10:36 -070090 /** Enum value for a server-initiated sync. */
91 public static final int SOURCE_SERVER = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
Dianne Hackborn231cc602009-04-27 17:10:36 -070093 /** Enum value for a local-initiated sync. */
94 public static final int SOURCE_LOCAL = 1;
95 /**
96 * Enum value for a poll-based sync (e.g., upon connection to
97 * network)
98 */
99 public static final int SOURCE_POLL = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Dianne Hackborn231cc602009-04-27 17:10:36 -0700101 /** Enum value for a user-initiated sync. */
102 public static final int SOURCE_USER = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800104 /** Enum value for a periodic sync. */
105 public static final int SOURCE_PERIODIC = 4;
106
Fred Quintana307da1a2010-01-21 14:24:20 -0800107 public static final long NOT_IN_BACKOFF_MODE = -1;
108
Dianne Hackborn043fcd92010-10-06 14:27:34 -0700109 public static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
Fred Quintanaac9385e2009-06-22 18:00:59 -0700110 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
111
Dianne Hackborn231cc602009-04-27 17:10:36 -0700112 // TODO: i18n -- grab these out of resources.
113 /** String names for the sync source types. */
114 public static final String[] SOURCES = { "SERVER",
115 "LOCAL",
116 "POLL",
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800117 "USER",
118 "PERIODIC" };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Dianne Hackborn231cc602009-04-27 17:10:36 -0700120 // The MESG column will contain one of these or one of the Error types.
121 public static final String MESG_SUCCESS = "success";
122 public static final String MESG_CANCELED = "canceled";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123
Dianne Hackborna33e3f72009-09-29 17:28:24 -0700124 public static final int MAX_HISTORY = 100;
Costin Manolache360e4542009-09-04 13:36:04 -0700125
Dianne Hackborn231cc602009-04-27 17:10:36 -0700126 private static final int MSG_WRITE_STATUS = 1;
127 private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
Costin Manolache360e4542009-09-04 13:36:04 -0700128
Dianne Hackborn231cc602009-04-27 17:10:36 -0700129 private static final int MSG_WRITE_STATISTICS = 2;
130 private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
Joe Onorato8294fad2009-07-15 16:08:44 -0700131
132 private static final boolean SYNC_ENABLED_DEFAULT = false;
Costin Manolache360e4542009-09-04 13:36:04 -0700133
Fred Quintanac2e46912010-03-15 16:10:44 -0700134 // the version of the accounts xml file format
Fred Quintanafb084402010-03-23 17:57:03 -0700135 private static final int ACCOUNTS_VERSION = 2;
136
137 private static HashMap<String, String> sAuthorityRenames;
138
139 static {
140 sAuthorityRenames = new HashMap<String, String>();
141 sAuthorityRenames.put("contacts", "com.android.contacts");
142 sAuthorityRenames.put("calendar", "com.android.calendar");
143 }
Fred Quintanac2e46912010-03-15 16:10:44 -0700144
Dianne Hackborn231cc602009-04-27 17:10:36 -0700145 public static class PendingOperation {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700146 final Account account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800147 final int userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700148 final int syncSource;
149 final String authority;
150 final Bundle extras; // note: read-only.
Fred Quintana307da1a2010-01-21 14:24:20 -0800151 final boolean expedited;
Costin Manolache360e4542009-09-04 13:36:04 -0700152
Dianne Hackborn231cc602009-04-27 17:10:36 -0700153 int authorityId;
154 byte[] flatExtras;
Costin Manolache360e4542009-09-04 13:36:04 -0700155
Amith Yamasani04e0d262012-02-14 11:50:53 -0800156 PendingOperation(Account account, int userId, int source,
Fred Quintana307da1a2010-01-21 14:24:20 -0800157 String authority, Bundle extras, boolean expedited) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700158 this.account = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800159 this.userId = userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700160 this.syncSource = source;
161 this.authority = authority;
162 this.extras = extras != null ? new Bundle(extras) : extras;
Fred Quintana307da1a2010-01-21 14:24:20 -0800163 this.expedited = expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700164 this.authorityId = -1;
165 }
166
167 PendingOperation(PendingOperation other) {
168 this.account = other.account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800169 this.userId = other.userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700170 this.syncSource = other.syncSource;
171 this.authority = other.authority;
172 this.extras = other.extras;
173 this.authorityId = other.authorityId;
Fred Quintana307da1a2010-01-21 14:24:20 -0800174 this.expedited = other.expedited;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 }
Costin Manolache360e4542009-09-04 13:36:04 -0700177
Dianne Hackborn231cc602009-04-27 17:10:36 -0700178 static class AccountInfo {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800179 final AccountAndUser accountAndUser;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700180 final HashMap<String, AuthorityInfo> authorities =
181 new HashMap<String, AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700182
Amith Yamasani04e0d262012-02-14 11:50:53 -0800183 AccountInfo(AccountAndUser accountAndUser) {
184 this.accountAndUser = accountAndUser;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700185 }
186 }
Costin Manolache360e4542009-09-04 13:36:04 -0700187
Dianne Hackborn231cc602009-04-27 17:10:36 -0700188 public static class AuthorityInfo {
Dianne Hackborn7a135592009-05-06 00:28:37 -0700189 final Account account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800190 final int userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700191 final String authority;
192 final int ident;
193 boolean enabled;
Fred Quintana5e787c42009-08-16 23:13:53 -0700194 int syncable;
Fred Quintana307da1a2010-01-21 14:24:20 -0800195 long backoffTime;
196 long backoffDelay;
197 long delayUntil;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800198 final ArrayList<Pair<Bundle, Long>> periodicSyncs;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700199
Amith Yamasani04e0d262012-02-14 11:50:53 -0800200 AuthorityInfo(Account account, int userId, String authority, int ident) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700201 this.account = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800202 this.userId = userId;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700203 this.authority = authority;
204 this.ident = ident;
Joe Onorato8294fad2009-07-15 16:08:44 -0700205 enabled = SYNC_ENABLED_DEFAULT;
Fred Quintana4a6679b2009-08-17 13:05:39 -0700206 syncable = -1; // default to "unknown"
Fred Quintana307da1a2010-01-21 14:24:20 -0800207 backoffTime = -1; // if < 0 then we aren't in backoff mode
208 backoffDelay = -1; // if < 0 then we aren't in backoff mode
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800209 periodicSyncs = new ArrayList<Pair<Bundle, Long>>();
210 periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS));
Dianne Hackborn231cc602009-04-27 17:10:36 -0700211 }
212 }
Costin Manolache360e4542009-09-04 13:36:04 -0700213
Dianne Hackborn231cc602009-04-27 17:10:36 -0700214 public static class SyncHistoryItem {
215 int authorityId;
216 int historyId;
217 long eventTime;
218 long elapsedTime;
219 int source;
220 int event;
221 long upstreamActivity;
222 long downstreamActivity;
223 String mesg;
Fred Quintanadc475562012-05-04 15:51:54 -0700224 boolean initialization;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700225 }
Costin Manolache360e4542009-09-04 13:36:04 -0700226
Dianne Hackborn231cc602009-04-27 17:10:36 -0700227 public static class DayStats {
228 public final int day;
229 public int successCount;
230 public long successTime;
231 public int failureCount;
232 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700233
Dianne Hackborn231cc602009-04-27 17:10:36 -0700234 public DayStats(int day) {
235 this.day = day;
236 }
237 }
Costin Manolache360e4542009-09-04 13:36:04 -0700238
Amith Yamasani04e0d262012-02-14 11:50:53 -0800239 interface OnSyncRequestListener {
240 /**
241 * Called when a sync is needed on an account(s) due to some change in state.
242 * @param account
243 * @param userId
244 * @param authority
245 * @param extras
246 */
247 public void onSyncRequest(Account account, int userId, String authority, Bundle extras);
248 }
249
Dianne Hackborn231cc602009-04-27 17:10:36 -0700250 // Primary list of all syncable authorities. Also our global lock.
251 private final SparseArray<AuthorityInfo> mAuthorities =
252 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700253
Amith Yamasani04e0d262012-02-14 11:50:53 -0800254 private final HashMap<AccountAndUser, AccountInfo> mAccounts
255 = new HashMap<AccountAndUser, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256
Dianne Hackborn231cc602009-04-27 17:10:36 -0700257 private final ArrayList<PendingOperation> mPendingOperations =
258 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700259
Amith Yamasani04e0d262012-02-14 11:50:53 -0800260 private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs
261 = new SparseArray<ArrayList<SyncInfo>>();
Costin Manolache360e4542009-09-04 13:36:04 -0700262
Dianne Hackborn231cc602009-04-27 17:10:36 -0700263 private final SparseArray<SyncStatusInfo> mSyncStatus =
264 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700265
Dianne Hackborn231cc602009-04-27 17:10:36 -0700266 private final ArrayList<SyncHistoryItem> mSyncHistory =
267 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700268
Dianne Hackborn231cc602009-04-27 17:10:36 -0700269 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
270 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700271
Fred Quintana77c560f2010-03-29 22:20:26 -0700272 private int mNextAuthorityId = 0;
273
Dianne Hackborn231cc602009-04-27 17:10:36 -0700274 // We keep 4 weeks of stats.
275 private final DayStats[] mDayStats = new DayStats[7*4];
276 private final Calendar mCal;
277 private int mYear;
278 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700279
Dianne Hackborn231cc602009-04-27 17:10:36 -0700280 private final Context mContext;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800281
Dianne Hackborn231cc602009-04-27 17:10:36 -0700282 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700283
Ashish Sharma69d95de2012-04-11 17:27:24 -0700284 private int mSyncRandomOffset;
285
Dianne Hackborn231cc602009-04-27 17:10:36 -0700286 /**
287 * This file contains the core engine state: all accounts and the
288 * settings for them. It must never be lost, and should be changed
289 * infrequently, so it is stored as an XML file.
290 */
291 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700292
Dianne Hackborn231cc602009-04-27 17:10:36 -0700293 /**
294 * This file contains the current sync status. We would like to retain
295 * it across boots, but its loss is not the end of the world, so we store
296 * this information as binary data.
297 */
298 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700299
Dianne Hackborn231cc602009-04-27 17:10:36 -0700300 /**
301 * This file contains sync statistics. This is purely debugging information
302 * so is written infrequently and can be thrown away at any time.
303 */
304 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700305
Dianne Hackborn231cc602009-04-27 17:10:36 -0700306 /**
307 * This file contains the pending sync operations. It is a binary file,
308 * which must be updated every time an operation is added or removed,
309 * so we have special handling of it.
310 */
311 private final AtomicFile mPendingFile;
312 private static final int PENDING_FINISH_TO_WRITE = 4;
313 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700314
Dianne Hackborn231cc602009-04-27 17:10:36 -0700315 private int mNextHistoryId = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800316 private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
317
318 private OnSyncRequestListener mSyncRequestListener;
Costin Manolache360e4542009-09-04 13:36:04 -0700319
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800320 private SyncStorageEngine(Context context, File dataDir) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700323
Dianne Hackborn231cc602009-04-27 17:10:36 -0700324 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700325
Dianne Hackborn231cc602009-04-27 17:10:36 -0700326 File systemDir = new File(dataDir, "system");
327 File syncDir = new File(systemDir, "sync");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800328 syncDir.mkdirs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700329 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
330 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
331 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
332 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700333
Dianne Hackborn231cc602009-04-27 17:10:36 -0700334 readAccountInfoLocked();
335 readStatusLocked();
336 readPendingOperationsLocked();
337 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -0700338 readAndDeleteLegacyAccountInfoLocked();
339 writeAccountInfoLocked();
340 writeStatusLocked();
341 writePendingOperationsLocked();
342 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344
345 public static SyncStorageEngine newTestInstance(Context context) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800346 return new SyncStorageEngine(context, context.getFilesDir());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
348
349 public static void init(Context context) {
350 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800351 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800353 // This call will return the correct directory whether Encrypted File Systems is
354 // enabled or not.
Jason parksa3cdaa52011-01-13 14:15:43 -0600355 File dataDir = Environment.getSecureDataDirectory();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800356 sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 }
358
359 public static SyncStorageEngine getSingleton() {
360 if (sSyncStorageEngine == null) {
361 throw new IllegalStateException("not initialized");
362 }
363 return sSyncStorageEngine;
364 }
365
Amith Yamasani04e0d262012-02-14 11:50:53 -0800366 protected void setOnSyncRequestListener(OnSyncRequestListener listener) {
367 if (mSyncRequestListener == null) {
368 mSyncRequestListener = listener;
369 }
370 }
371
Dianne Hackborn231cc602009-04-27 17:10:36 -0700372 @Override public void handleMessage(Message msg) {
373 if (msg.what == MSG_WRITE_STATUS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700374 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700375 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700376 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700377 } else if (msg.what == MSG_WRITE_STATISTICS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700378 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700379 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 }
381 }
382 }
Costin Manolache360e4542009-09-04 13:36:04 -0700383
Ashish Sharma69d95de2012-04-11 17:27:24 -0700384 public int getSyncRandomOffset() {
385 return mSyncRandomOffset;
386 }
387
Dianne Hackborn231cc602009-04-27 17:10:36 -0700388 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
389 synchronized (mAuthorities) {
390 mChangeListeners.register(callback, mask);
391 }
392 }
Costin Manolache360e4542009-09-04 13:36:04 -0700393
Dianne Hackborn231cc602009-04-27 17:10:36 -0700394 public void removeStatusChangeListener(ISyncStatusObserver callback) {
395 synchronized (mAuthorities) {
396 mChangeListeners.unregister(callback);
397 }
398 }
Costin Manolache360e4542009-09-04 13:36:04 -0700399
Dianne Hackborn231cc602009-04-27 17:10:36 -0700400 private void reportChange(int which) {
401 ArrayList<ISyncStatusObserver> reports = null;
402 synchronized (mAuthorities) {
403 int i = mChangeListeners.beginBroadcast();
404 while (i > 0) {
405 i--;
406 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
407 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 continue;
409 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700410 if (reports == null) {
411 reports = new ArrayList<ISyncStatusObserver>(i);
412 }
413 reports.add(mChangeListeners.getBroadcastItem(i));
414 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700415 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700416 }
Costin Manolache360e4542009-09-04 13:36:04 -0700417
Fred Quintana77c560f2010-03-29 22:20:26 -0700418 if (Log.isLoggable(TAG, Log.VERBOSE)) {
419 Log.v(TAG, "reportChange " + which + " to: " + reports);
420 }
Costin Manolache360e4542009-09-04 13:36:04 -0700421
Dianne Hackborn231cc602009-04-27 17:10:36 -0700422 if (reports != null) {
423 int i = reports.size();
424 while (i > 0) {
425 i--;
426 try {
427 reports.get(i).onStatusChanged(which);
428 } catch (RemoteException e) {
429 // The remote callback list will take care of this for us.
430 }
431 }
432 }
433 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700434
Amith Yamasani04e0d262012-02-14 11:50:53 -0800435 public boolean getSyncAutomatically(Account account, int userId, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700436 synchronized (mAuthorities) {
437 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800438 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700439 "getSyncAutomatically");
440 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700441 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700442
Dianne Hackborn231cc602009-04-27 17:10:36 -0700443 int i = mAuthorities.size();
444 while (i > 0) {
445 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700446 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700447 if (authority.authority.equals(providerName)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800448 && authority.userId == userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700449 && authority.enabled) {
450 return true;
451 }
452 }
453 return false;
454 }
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456
Amith Yamasani04e0d262012-02-14 11:50:53 -0800457 public void setSyncAutomatically(Account account, int userId, String providerName,
458 boolean sync) {
459 Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
460 + ", user " + userId + " -> " + sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700461 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800462 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
463 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700464 if (authority.enabled == sync) {
465 Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
466 return;
467 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700468 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700469 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700471
Fred Quintana77c560f2010-03-29 22:20:26 -0700472 if (sync) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800473 requestSync(account, userId, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700474 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700475 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
477
Amith Yamasani04e0d262012-02-14 11:50:53 -0800478 public int getIsSyncable(Account account, int userId, String providerName) {
Fred Quintana5e787c42009-08-16 23:13:53 -0700479 synchronized (mAuthorities) {
480 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800481 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintana5e787c42009-08-16 23:13:53 -0700482 "getIsSyncable");
483 if (authority == null) {
484 return -1;
485 }
486 return authority.syncable;
487 }
488
489 int i = mAuthorities.size();
490 while (i > 0) {
491 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700492 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700493 if (authority.authority.equals(providerName)) {
494 return authority.syncable;
495 }
496 }
497 return -1;
498 }
499 }
500
Amith Yamasani04e0d262012-02-14 11:50:53 -0800501 public void setIsSyncable(Account account, int userId, String providerName, int syncable) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700502 if (syncable > 1) {
503 syncable = 1;
504 } else if (syncable < -1) {
505 syncable = -1;
506 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800507 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName
508 + ", user " + userId + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700509 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800510 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
511 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700512 if (authority.syncable == syncable) {
513 Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
514 return;
515 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700516 authority.syncable = syncable;
517 writeAccountInfoLocked();
518 }
519
Fred Quintana77c560f2010-03-29 22:20:26 -0700520 if (syncable > 0) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800521 requestSync(account, userId, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700522 }
523 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
524 }
525
Amith Yamasani04e0d262012-02-14 11:50:53 -0800526 public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800527 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800528 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
529 "getBackoff");
Fred Quintana307da1a2010-01-21 14:24:20 -0800530 if (authority == null || authority.backoffTime < 0) {
531 return null;
532 }
533 return Pair.create(authority.backoffTime, authority.backoffDelay);
534 }
535 }
536
Amith Yamasani04e0d262012-02-14 11:50:53 -0800537 public void setBackoff(Account account, int userId, String providerName,
Fred Quintana307da1a2010-01-21 14:24:20 -0800538 long nextSyncTime, long nextDelay) {
539 if (Log.isLoggable(TAG, Log.VERBOSE)) {
540 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800541 + ", user " + userId
Fred Quintana307da1a2010-01-21 14:24:20 -0800542 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
543 }
544 boolean changed = false;
545 synchronized (mAuthorities) {
546 if (account == null || providerName == null) {
547 for (AccountInfo accountInfo : mAccounts.values()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800548 if (account != null && !account.equals(accountInfo.accountAndUser.account)
549 && userId != accountInfo.accountAndUser.userId) {
550 continue;
551 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800552 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
553 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
554 continue;
555 }
556 if (authorityInfo.backoffTime != nextSyncTime
557 || authorityInfo.backoffDelay != nextDelay) {
558 authorityInfo.backoffTime = nextSyncTime;
559 authorityInfo.backoffDelay = nextDelay;
560 changed = true;
561 }
562 }
563 }
564 } else {
565 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800566 getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */,
567 true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800568 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
569 return;
570 }
571 authority.backoffTime = nextSyncTime;
572 authority.backoffDelay = nextDelay;
573 changed = true;
574 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800575 }
576
577 if (changed) {
578 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
579 }
580 }
581
Alon Alberted1d2532011-02-15 14:02:14 -0800582 public void clearAllBackoffs(SyncQueue syncQueue) {
Alon Albert744e310f2010-12-14 11:37:20 -0800583 boolean changed = false;
584 synchronized (mAuthorities) {
585 for (AccountInfo accountInfo : mAccounts.values()) {
586 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
587 if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
588 || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
589 if (Log.isLoggable(TAG, Log.VERBOSE)) {
590 Log.v(TAG, "clearAllBackoffs:"
591 + " authority:" + authorityInfo.authority
Amith Yamasani04e0d262012-02-14 11:50:53 -0800592 + " account:" + accountInfo.accountAndUser.account.name
593 + " user:" + accountInfo.accountAndUser.userId
Alon Albert744e310f2010-12-14 11:37:20 -0800594 + " backoffTime was: " + authorityInfo.backoffTime
595 + " backoffDelay was: " + authorityInfo.backoffDelay);
596 }
597 authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
598 authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800599 syncQueue.onBackoffChanged(accountInfo.accountAndUser.account,
600 accountInfo.accountAndUser.userId, authorityInfo.authority, 0);
Alon Albert744e310f2010-12-14 11:37:20 -0800601 changed = true;
602 }
603 }
604 }
605 }
606
607 if (changed) {
608 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
609 }
610 }
611
Amith Yamasani04e0d262012-02-14 11:50:53 -0800612 public void setDelayUntilTime(Account account, int userId, String providerName,
613 long delayUntil) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800614 if (Log.isLoggable(TAG, Log.VERBOSE)) {
615 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800616 + ", user " + userId + " -> delayUntil " + delayUntil);
Fred Quintana307da1a2010-01-21 14:24:20 -0800617 }
618 synchronized (mAuthorities) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800619 AuthorityInfo authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800620 account, userId, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800621 if (authority.delayUntil == delayUntil) {
622 return;
623 }
624 authority.delayUntil = delayUntil;
Fred Quintana307da1a2010-01-21 14:24:20 -0800625 }
626
627 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
628 }
629
Amith Yamasani04e0d262012-02-14 11:50:53 -0800630 public long getDelayUntilTime(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800631 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800632 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
633 "getDelayUntil");
Fred Quintana307da1a2010-01-21 14:24:20 -0800634 if (authority == null) {
635 return 0;
636 }
637 return authority.delayUntil;
638 }
639 }
640
Amith Yamasani04e0d262012-02-14 11:50:53 -0800641 private void updateOrRemovePeriodicSync(Account account, int userId, String providerName,
642 Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800643 long period, boolean add) {
644 if (period <= 0) {
645 period = 0;
646 }
647 if (extras == null) {
648 extras = new Bundle();
649 }
650 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800651 Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId
652 + ", provider " + providerName
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800653 + " -> period " + period + ", extras " + extras);
654 }
655 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700656 try {
657 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800658 getOrCreateAuthorityLocked(account, userId, providerName, -1, false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700659 if (add) {
660 // add this periodic sync if one with the same extras doesn't already
661 // exist in the periodicSyncs array
662 boolean alreadyPresent = false;
663 for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
664 Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
665 final Bundle existingExtras = syncInfo.first;
666 if (equals(existingExtras, extras)) {
667 if (syncInfo.second == period) {
668 return;
669 }
670 authority.periodicSyncs.set(i, Pair.create(extras, period));
671 alreadyPresent = true;
672 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800673 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700674 }
675 // if we added an entry to the periodicSyncs array also add an entry to
676 // the periodic syncs status to correspond to it
677 if (!alreadyPresent) {
678 authority.periodicSyncs.add(Pair.create(extras, period));
679 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
680 status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
681 }
682 } else {
683 // remove any periodic syncs that match the authority and extras
684 SyncStatusInfo status = mSyncStatus.get(authority.ident);
685 boolean changed = false;
686 Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
687 int i = 0;
688 while (iterator.hasNext()) {
689 Pair<Bundle, Long> syncInfo = iterator.next();
690 if (equals(syncInfo.first, extras)) {
691 iterator.remove();
692 changed = true;
693 // if we removed an entry from the periodicSyncs array also
694 // remove the corresponding entry from the status
695 if (status != null) {
696 status.removePeriodicSyncTime(i);
697 }
698 } else {
699 i++;
700 }
701 }
702 if (!changed) {
703 return;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800704 }
705 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700706 } finally {
707 writeAccountInfoLocked();
708 writeStatusLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800709 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800710 }
711
712 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
713 }
714
Amith Yamasani04e0d262012-02-14 11:50:53 -0800715 public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800716 long pollFrequency) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800717 updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency,
718 true /* add */);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800719 }
720
Amith Yamasani04e0d262012-02-14 11:50:53 -0800721 public void removePeriodicSync(Account account, int userId, String providerName,
722 Bundle extras) {
723 updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800724 false /* remove */);
725 }
726
Amith Yamasani04e0d262012-02-14 11:50:53 -0800727 public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800728 ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
729 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800730 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
731 "getPeriodicSyncs");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800732 if (authority != null) {
733 for (Pair<Bundle, Long> item : authority.periodicSyncs) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800734 syncs.add(new PeriodicSync(account, providerName, item.first,
735 item.second));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800736 }
737 }
738 }
739 return syncs;
740 }
741
Amith Yamasani04e0d262012-02-14 11:50:53 -0800742 public void setMasterSyncAutomatically(boolean flag, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700743 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800744 Boolean auto = mMasterSyncAutomatically.get(userId);
745 if (auto != null && (boolean) auto == flag) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700746 return;
747 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800748 mMasterSyncAutomatically.put(userId, flag);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700749 writeAccountInfoLocked();
750 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700751 if (flag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800752 requestSync(null, userId, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700753 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700754 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
755 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700756 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757
Amith Yamasani04e0d262012-02-14 11:50:53 -0800758 public boolean getMasterSyncAutomatically(int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700759 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800760 Boolean auto = mMasterSyncAutomatically.get(userId);
761 return auto == null ? true : auto;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700762 }
763 }
Costin Manolache360e4542009-09-04 13:36:04 -0700764
Amith Yamasani04e0d262012-02-14 11:50:53 -0800765 public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700766 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800767 return getOrCreateAuthorityLocked(account, userId, authority,
Fred Quintana1bbcd102010-02-10 10:04:33 -0800768 -1 /* assign a new identifier if creating a new authority */,
769 true /* write to storage if this results in a change */);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700770 }
771 }
Costin Manolache360e4542009-09-04 13:36:04 -0700772
Amith Yamasani04e0d262012-02-14 11:50:53 -0800773 public void removeAuthority(Account account, int userId, String authority) {
Fred Quintana7620f1a2010-03-16 15:58:44 -0700774 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800775 removeAuthorityLocked(account, userId, authority, true /* doWrite */);
Fred Quintana7620f1a2010-03-16 15:58:44 -0700776 }
777 }
778
Dianne Hackborn231cc602009-04-27 17:10:36 -0700779 public AuthorityInfo getAuthority(int authorityId) {
780 synchronized (mAuthorities) {
781 return mAuthorities.get(authorityId);
782 }
783 }
Costin Manolache360e4542009-09-04 13:36:04 -0700784
Dianne Hackborn231cc602009-04-27 17:10:36 -0700785 /**
786 * Returns true if there is currently a sync operation for the given
Fred Quintana918339a2010-10-05 14:00:39 -0700787 * account or authority actively being processed.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700788 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800789 public boolean isSyncActive(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700790 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800791 for (SyncInfo syncInfo : getCurrentSyncs(userId)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700792 AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700793 if (ainfo != null && ainfo.account.equals(account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800794 && ainfo.authority.equals(authority)
795 && ainfo.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700796 return true;
797 }
798 }
799 }
Costin Manolache360e4542009-09-04 13:36:04 -0700800
Dianne Hackborn231cc602009-04-27 17:10:36 -0700801 return false;
802 }
Costin Manolache360e4542009-09-04 13:36:04 -0700803
Dianne Hackborn231cc602009-04-27 17:10:36 -0700804 public PendingOperation insertIntoPending(PendingOperation op) {
805 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700806 if (Log.isLoggable(TAG, Log.VERBOSE)) {
807 Log.v(TAG, "insertIntoPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800808 + " user=" + op.userId
809 + " auth=" + op.authority
810 + " src=" + op.syncSource
811 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700812 }
Costin Manolache360e4542009-09-04 13:36:04 -0700813
Amith Yamasani04e0d262012-02-14 11:50:53 -0800814 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700815 op.authority,
816 -1 /* desired identifier */,
817 true /* write accounts to storage */);
818 if (authority == null) {
819 return null;
820 }
Costin Manolache360e4542009-09-04 13:36:04 -0700821
Dianne Hackborn231cc602009-04-27 17:10:36 -0700822 op = new PendingOperation(op);
823 op.authorityId = authority.ident;
824 mPendingOperations.add(op);
825 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700826
Dianne Hackborn231cc602009-04-27 17:10:36 -0700827 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
828 status.pending = true;
829 }
Costin Manolache360e4542009-09-04 13:36:04 -0700830
Fred Quintanaac9385e2009-06-22 18:00:59 -0700831 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700832 return op;
833 }
834
835 public boolean deleteFromPending(PendingOperation op) {
836 boolean res = false;
837 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700838 if (Log.isLoggable(TAG, Log.VERBOSE)) {
839 Log.v(TAG, "deleteFromPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800840 + " user=" + op.userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700841 + " auth=" + op.authority
842 + " src=" + op.syncSource
843 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700844 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700845 if (mPendingOperations.remove(op)) {
846 if (mPendingOperations.size() == 0
847 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
848 writePendingOperationsLocked();
849 mNumPendingFinished = 0;
850 } else {
851 mNumPendingFinished++;
852 }
Costin Manolache360e4542009-09-04 13:36:04 -0700853
Amith Yamasani04e0d262012-02-14 11:50:53 -0800854 AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700855 "deleteFromPending");
856 if (authority != null) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700857 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700858 final int N = mPendingOperations.size();
859 boolean morePending = false;
860 for (int i=0; i<N; i++) {
861 PendingOperation cur = mPendingOperations.get(i);
862 if (cur.account.equals(op.account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800863 && cur.authority.equals(op.authority)
864 && cur.userId == op.userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700865 morePending = true;
866 break;
867 }
868 }
Costin Manolache360e4542009-09-04 13:36:04 -0700869
Dianne Hackborn231cc602009-04-27 17:10:36 -0700870 if (!morePending) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700871 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700872 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
873 status.pending = false;
874 }
875 }
Costin Manolache360e4542009-09-04 13:36:04 -0700876
Dianne Hackborn231cc602009-04-27 17:10:36 -0700877 res = true;
878 }
879 }
Costin Manolache360e4542009-09-04 13:36:04 -0700880
Fred Quintanaac9385e2009-06-22 18:00:59 -0700881 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700882 return res;
883 }
884
Dianne Hackborn231cc602009-04-27 17:10:36 -0700885 /**
886 * Return a copy of the current array of pending operations. The
887 * PendingOperation objects are the real objects stored inside, so that
888 * they can be used with deleteFromPending().
889 */
890 public ArrayList<PendingOperation> getPendingOperations() {
891 synchronized (mAuthorities) {
892 return new ArrayList<PendingOperation>(mPendingOperations);
893 }
894 }
Costin Manolache360e4542009-09-04 13:36:04 -0700895
Dianne Hackborn231cc602009-04-27 17:10:36 -0700896 /**
897 * Return the number of currently pending operations.
898 */
899 public int getPendingOperationCount() {
900 synchronized (mAuthorities) {
901 return mPendingOperations.size();
902 }
903 }
Costin Manolache360e4542009-09-04 13:36:04 -0700904
Dianne Hackborn231cc602009-04-27 17:10:36 -0700905 /**
906 * Called when the set of account has changed, given the new array of
907 * active accounts.
908 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800909 public void doDatabaseCleanup(Account[] accounts, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700910 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700911 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts...");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700912 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
913 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
914 while (accIt.hasNext()) {
915 AccountInfo acc = accIt.next();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800916 if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
917 && acc.accountAndUser.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700918 // This account no longer exists...
Fred Quintana77c560f2010-03-29 22:20:26 -0700919 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800920 Log.w(TAG, "Account removed: " + acc.accountAndUser);
Fred Quintana77c560f2010-03-29 22:20:26 -0700921 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700922 for (AuthorityInfo auth : acc.authorities.values()) {
923 removing.put(auth.ident, auth);
924 }
925 accIt.remove();
926 }
927 }
Costin Manolache360e4542009-09-04 13:36:04 -0700928
Dianne Hackborn231cc602009-04-27 17:10:36 -0700929 // Clean out all data structures.
930 int i = removing.size();
931 if (i > 0) {
932 while (i > 0) {
933 i--;
934 int ident = removing.keyAt(i);
935 mAuthorities.remove(ident);
936 int j = mSyncStatus.size();
937 while (j > 0) {
938 j--;
939 if (mSyncStatus.keyAt(j) == ident) {
940 mSyncStatus.remove(mSyncStatus.keyAt(j));
941 }
942 }
943 j = mSyncHistory.size();
944 while (j > 0) {
945 j--;
946 if (mSyncHistory.get(j).authorityId == ident) {
947 mSyncHistory.remove(j);
948 }
949 }
950 }
951 writeAccountInfoLocked();
952 writeStatusLocked();
953 writePendingOperationsLocked();
954 writeStatisticsLocked();
955 }
956 }
957 }
958
959 /**
Fred Quintana918339a2010-10-05 14:00:39 -0700960 * Called when a sync is starting. Supply a valid ActiveSyncContext with information
961 * about the sync.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700962 */
Fred Quintana918339a2010-10-05 14:00:39 -0700963 public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
964 final SyncInfo syncInfo;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700965 synchronized (mAuthorities) {
Fred Quintana918339a2010-10-05 14:00:39 -0700966 if (Log.isLoggable(TAG, Log.VERBOSE)) {
967 Log.v(TAG, "setActiveSync: account="
968 + activeSyncContext.mSyncOperation.account
969 + " auth=" + activeSyncContext.mSyncOperation.authority
970 + " src=" + activeSyncContext.mSyncOperation.syncSource
971 + " extras=" + activeSyncContext.mSyncOperation.extras);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700972 }
Fred Quintana918339a2010-10-05 14:00:39 -0700973 AuthorityInfo authority = getOrCreateAuthorityLocked(
974 activeSyncContext.mSyncOperation.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800975 activeSyncContext.mSyncOperation.userId,
Fred Quintana918339a2010-10-05 14:00:39 -0700976 activeSyncContext.mSyncOperation.authority,
977 -1 /* assign a new identifier if creating a new authority */,
978 true /* write to storage if this results in a change */);
979 syncInfo = new SyncInfo(authority.ident,
980 authority.account, authority.authority,
981 activeSyncContext.mStartTime);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800982 getCurrentSyncs(authority.userId).add(syncInfo);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700983 }
Costin Manolache360e4542009-09-04 13:36:04 -0700984
Fred Quintana918339a2010-10-05 14:00:39 -0700985 reportActiveChange();
986 return syncInfo;
987 }
988
989 /**
990 * Called to indicate that a previously active sync is no longer active.
991 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800992 public void removeActiveSync(SyncInfo syncInfo, int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -0700993 synchronized (mAuthorities) {
994 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800995 Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
996 + " user=" + userId
997 + " auth=" + syncInfo.authority);
Fred Quintana918339a2010-10-05 14:00:39 -0700998 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800999 getCurrentSyncs(userId).remove(syncInfo);
Fred Quintana918339a2010-10-05 14:00:39 -07001000 }
1001
1002 reportActiveChange();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001003 }
1004
1005 /**
1006 * To allow others to send active change reports, to poke clients.
1007 */
1008 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001009 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001010 }
Costin Manolache360e4542009-09-04 13:36:04 -07001011
Dianne Hackborn231cc602009-04-27 17:10:36 -07001012 /**
1013 * Note that sync has started for the given account and authority.
1014 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001015 public long insertStartSyncEvent(Account accountName, int userId, String authorityName,
Fred Quintanadc475562012-05-04 15:51:54 -07001016 long now, int source, boolean initialization) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001017 long id;
1018 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001019 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001020 Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId
Dianne Hackborn231cc602009-04-27 17:10:36 -07001021 + " auth=" + authorityName + " source=" + source);
Fred Quintana77c560f2010-03-29 22:20:26 -07001022 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001023 AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001024 "insertStartSyncEvent");
1025 if (authority == null) {
1026 return -1;
1027 }
1028 SyncHistoryItem item = new SyncHistoryItem();
Fred Quintanadc475562012-05-04 15:51:54 -07001029 item.initialization = initialization;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001030 item.authorityId = authority.ident;
1031 item.historyId = mNextHistoryId++;
1032 if (mNextHistoryId < 0) mNextHistoryId = 0;
1033 item.eventTime = now;
1034 item.source = source;
1035 item.event = EVENT_START;
1036 mSyncHistory.add(0, item);
1037 while (mSyncHistory.size() > MAX_HISTORY) {
1038 mSyncHistory.remove(mSyncHistory.size()-1);
1039 }
1040 id = item.historyId;
Fred Quintana77c560f2010-03-29 22:20:26 -07001041 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001042 }
Costin Manolache360e4542009-09-04 13:36:04 -07001043
Fred Quintanaac9385e2009-06-22 18:00:59 -07001044 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001045 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
1047
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001048 public static boolean equals(Bundle b1, Bundle b2) {
1049 if (b1.size() != b2.size()) {
1050 return false;
1051 }
1052 if (b1.isEmpty()) {
1053 return true;
1054 }
1055 for (String key : b1.keySet()) {
1056 if (!b2.containsKey(key)) {
1057 return false;
1058 }
1059 if (!b1.get(key).equals(b2.get(key))) {
1060 return false;
1061 }
1062 }
1063 return true;
1064 }
1065
Fred Quintana77c560f2010-03-29 22:20:26 -07001066 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001068 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001069 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1070 Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
1071 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001072 SyncHistoryItem item = null;
1073 int i = mSyncHistory.size();
1074 while (i > 0) {
1075 i--;
1076 item = mSyncHistory.get(i);
1077 if (item.historyId == historyId) {
1078 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001080 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 }
Costin Manolache360e4542009-09-04 13:36:04 -07001082
Dianne Hackborn231cc602009-04-27 17:10:36 -07001083 if (item == null) {
1084 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
1085 return;
1086 }
Costin Manolache360e4542009-09-04 13:36:04 -07001087
Dianne Hackborn231cc602009-04-27 17:10:36 -07001088 item.elapsedTime = elapsedTime;
1089 item.event = EVENT_STOP;
1090 item.mesg = resultMessage;
1091 item.downstreamActivity = downstreamActivity;
1092 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -07001093
Dianne Hackborn231cc602009-04-27 17:10:36 -07001094 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -07001095
Dianne Hackborn231cc602009-04-27 17:10:36 -07001096 status.numSyncs++;
1097 status.totalElapsedTime += elapsedTime;
1098 switch (item.source) {
1099 case SOURCE_LOCAL:
1100 status.numSourceLocal++;
1101 break;
1102 case SOURCE_POLL:
1103 status.numSourcePoll++;
1104 break;
1105 case SOURCE_USER:
1106 status.numSourceUser++;
1107 break;
1108 case SOURCE_SERVER:
1109 status.numSourceServer++;
1110 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001111 case SOURCE_PERIODIC:
1112 status.numSourcePeriodic++;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001113 break;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001114 }
Costin Manolache360e4542009-09-04 13:36:04 -07001115
Dianne Hackborn231cc602009-04-27 17:10:36 -07001116 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -07001117 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001118 if (mDayStats[0] == null) {
1119 mDayStats[0] = new DayStats(day);
1120 } else if (day != mDayStats[0].day) {
1121 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
1122 mDayStats[0] = new DayStats(day);
1123 writeStatisticsNow = true;
1124 } else if (mDayStats[0] == null) {
1125 }
1126 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -07001127
Dianne Hackborn231cc602009-04-27 17:10:36 -07001128 final long lastSyncTime = (item.eventTime + elapsedTime);
1129 boolean writeStatusNow = false;
1130 if (MESG_SUCCESS.equals(resultMessage)) {
1131 // - if successful, update the successful columns
1132 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
1133 writeStatusNow = true;
1134 }
1135 status.lastSuccessTime = lastSyncTime;
1136 status.lastSuccessSource = item.source;
1137 status.lastFailureTime = 0;
1138 status.lastFailureSource = -1;
1139 status.lastFailureMesg = null;
1140 status.initialFailureTime = 0;
1141 ds.successCount++;
1142 ds.successTime += elapsedTime;
1143 } else if (!MESG_CANCELED.equals(resultMessage)) {
1144 if (status.lastFailureTime == 0) {
1145 writeStatusNow = true;
1146 }
1147 status.lastFailureTime = lastSyncTime;
1148 status.lastFailureSource = item.source;
1149 status.lastFailureMesg = resultMessage;
1150 if (status.initialFailureTime == 0) {
1151 status.initialFailureTime = lastSyncTime;
1152 }
1153 ds.failureCount++;
1154 ds.failureTime += elapsedTime;
1155 }
Costin Manolache360e4542009-09-04 13:36:04 -07001156
Dianne Hackborn231cc602009-04-27 17:10:36 -07001157 if (writeStatusNow) {
1158 writeStatusLocked();
1159 } else if (!hasMessages(MSG_WRITE_STATUS)) {
1160 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
1161 WRITE_STATUS_DELAY);
1162 }
1163 if (writeStatisticsNow) {
1164 writeStatisticsLocked();
1165 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
1166 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
1167 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -07001168 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001169 }
Costin Manolache360e4542009-09-04 13:36:04 -07001170
Fred Quintanaac9385e2009-06-22 18:00:59 -07001171 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001172 }
1173
1174 /**
Fred Quintana918339a2010-10-05 14:00:39 -07001175 * Return a list of the currently active syncs. Note that the returned items are the
1176 * real, live active sync objects, so be careful what you do with it.
1177 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001178 public List<SyncInfo> getCurrentSyncs(int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -07001179 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001180 ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId);
1181 if (syncs == null) {
1182 syncs = new ArrayList<SyncInfo>();
1183 mCurrentSyncs.put(userId, syncs);
1184 }
Amith Yamasani1b6ae002012-03-14 14:53:36 -07001185 return syncs;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001186 }
1187 }
Costin Manolache360e4542009-09-04 13:36:04 -07001188
Dianne Hackborn231cc602009-04-27 17:10:36 -07001189 /**
1190 * Return an array of the current sync status for all authorities. Note
1191 * that the objects inside the array are the real, live status objects,
1192 * so be careful what you do with them.
1193 */
1194 public ArrayList<SyncStatusInfo> getSyncStatus() {
1195 synchronized (mAuthorities) {
1196 final int N = mSyncStatus.size();
1197 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
1198 for (int i=0; i<N; i++) {
1199 ops.add(mSyncStatus.valueAt(i));
1200 }
1201 return ops;
1202 }
1203 }
Costin Manolache360e4542009-09-04 13:36:04 -07001204
Dianne Hackborn231cc602009-04-27 17:10:36 -07001205 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001206 * Return an array of the current authorities. Note
1207 * that the objects inside the array are the real, live objects,
1208 * so be careful what you do with them.
1209 */
1210 public ArrayList<AuthorityInfo> getAuthorities() {
1211 synchronized (mAuthorities) {
1212 final int N = mAuthorities.size();
1213 ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
1214 for (int i=0; i<N; i++) {
1215 infos.add(mAuthorities.valueAt(i));
1216 }
1217 return infos;
1218 }
1219 }
1220
1221 /**
Costin Manolacheb7520982009-09-02 18:03:05 -07001222 * Returns the status that matches the authority and account.
1223 *
1224 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -07001225 * @param authority the authority whose row should be selected
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001226 * @return the SyncStatusInfo for the authority
Dianne Hackborn231cc602009-04-27 17:10:36 -07001227 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001228 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId,
1229 String authority) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001230 if (account == null || authority == null) {
1231 throw new IllegalArgumentException();
1232 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001233 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001234 final int N = mSyncStatus.size();
1235 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001236 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001237 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -07001238
Amith Yamasani04e0d262012-02-14 11:50:53 -08001239 if (ainfo != null && ainfo.authority.equals(authority)
1240 && ainfo.userId == userId
1241 && account.equals(ainfo.account)) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001242 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001243 }
1244 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001245 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001246 }
1247 }
Costin Manolache360e4542009-09-04 13:36:04 -07001248
Dianne Hackborn231cc602009-04-27 17:10:36 -07001249 /**
1250 * Return true if the pending status is true of any matching authorities.
1251 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001252 public boolean isSyncPending(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001253 synchronized (mAuthorities) {
1254 final int N = mSyncStatus.size();
1255 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001256 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001257 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
1258 if (ainfo == null) {
1259 continue;
1260 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001261 if (userId != ainfo.userId) {
1262 continue;
1263 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001264 if (account != null && !ainfo.account.equals(account)) {
1265 continue;
1266 }
1267 if (ainfo.authority.equals(authority) && cur.pending) {
1268 return true;
1269 }
1270 }
1271 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 }
1273 }
1274
1275 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001276 * Return an array of the current sync status for all authorities. Note
1277 * that the objects inside the array are the real, live status objects,
1278 * so be careful what you do with them.
1279 */
1280 public ArrayList<SyncHistoryItem> getSyncHistory() {
1281 synchronized (mAuthorities) {
1282 final int N = mSyncHistory.size();
1283 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1284 for (int i=0; i<N; i++) {
1285 items.add(mSyncHistory.get(i));
1286 }
1287 return items;
1288 }
1289 }
Costin Manolache360e4542009-09-04 13:36:04 -07001290
Dianne Hackborn231cc602009-04-27 17:10:36 -07001291 /**
1292 * Return an array of the current per-day statistics. Note
1293 * that the objects inside the array are the real, live status objects,
1294 * so be careful what you do with them.
1295 */
1296 public DayStats[] getDayStatistics() {
1297 synchronized (mAuthorities) {
1298 DayStats[] ds = new DayStats[mDayStats.length];
1299 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1300 return ds;
1301 }
1302 }
Costin Manolache360e4542009-09-04 13:36:04 -07001303
Dianne Hackborn55280a92009-05-07 15:53:46 -07001304 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001305 mCal.setTimeInMillis(System.currentTimeMillis());
1306 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1307 if (mYear != mCal.get(Calendar.YEAR)) {
1308 mYear = mCal.get(Calendar.YEAR);
1309 mCal.clear();
1310 mCal.set(Calendar.YEAR, mYear);
1311 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1312 }
1313 return dayOfYear + mYearInDays;
1314 }
Costin Manolache360e4542009-09-04 13:36:04 -07001315
Dianne Hackborn231cc602009-04-27 17:10:36 -07001316 /**
1317 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001318 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001319 * @param accountName The name of the account for the authority.
1320 * @param authorityName The name of the authority itself.
1321 * @param tag If non-null, this will be used in a log message if the
1322 * requested authority does not exist.
1323 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001324 private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001325 String tag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001326 AccountAndUser au = new AccountAndUser(accountName, userId);
1327 AccountInfo accountInfo = mAccounts.get(au);
1328 if (accountInfo == null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001329 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001330 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001331 Log.v(TAG, tag + ": unknown account " + au);
Fred Quintanab763ab22009-08-18 18:07:30 -07001332 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001333 }
1334 return null;
1335 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001336 AuthorityInfo authority = accountInfo.authorities.get(authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001337 if (authority == null) {
1338 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001339 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1340 Log.v(TAG, tag + ": unknown authority " + authorityName);
1341 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001342 }
1343 return null;
1344 }
Costin Manolache360e4542009-09-04 13:36:04 -07001345
Dianne Hackborn231cc602009-04-27 17:10:36 -07001346 return authority;
1347 }
Costin Manolache360e4542009-09-04 13:36:04 -07001348
Amith Yamasani04e0d262012-02-14 11:50:53 -08001349 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001350 String authorityName, int ident, boolean doWrite) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001351 AccountAndUser au = new AccountAndUser(accountName, userId);
1352 AccountInfo account = mAccounts.get(au);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001353 if (account == null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001354 account = new AccountInfo(au);
1355 mAccounts.put(au, account);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001356 }
1357 AuthorityInfo authority = account.authorities.get(authorityName);
1358 if (authority == null) {
1359 if (ident < 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001360 ident = mNextAuthorityId;
1361 mNextAuthorityId++;
1362 doWrite = true;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001363 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001364 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1365 Log.v(TAG, "created a new AuthorityInfo for " + accountName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001366 + ", user " + userId
1367 + ", provider " + authorityName);
Fred Quintana77c560f2010-03-29 22:20:26 -07001368 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001369 authority = new AuthorityInfo(accountName, userId, authorityName, ident);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001370 account.authorities.put(authorityName, authority);
1371 mAuthorities.put(ident, authority);
1372 if (doWrite) {
1373 writeAccountInfoLocked();
1374 }
1375 }
Costin Manolache360e4542009-09-04 13:36:04 -07001376
Dianne Hackborn231cc602009-04-27 17:10:36 -07001377 return authority;
1378 }
Costin Manolache360e4542009-09-04 13:36:04 -07001379
Amith Yamasani04e0d262012-02-14 11:50:53 -08001380 private void removeAuthorityLocked(Account account, int userId, String authorityName,
1381 boolean doWrite) {
1382 AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
Fred Quintana7620f1a2010-03-16 15:58:44 -07001383 if (accountInfo != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001384 final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
1385 if (authorityInfo != null) {
1386 mAuthorities.remove(authorityInfo.ident);
Fred Quintana77c560f2010-03-29 22:20:26 -07001387 if (doWrite) {
1388 writeAccountInfoLocked();
1389 }
Fred Quintana7620f1a2010-03-16 15:58:44 -07001390 }
1391 }
1392 }
1393
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001394 public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
1395 synchronized (mAuthorities) {
1396 return getOrCreateSyncStatusLocked(authority.ident);
1397 }
1398 }
1399
Dianne Hackborn231cc602009-04-27 17:10:36 -07001400 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1401 SyncStatusInfo status = mSyncStatus.get(authorityId);
1402 if (status == null) {
1403 status = new SyncStatusInfo(authorityId);
1404 mSyncStatus.put(authorityId, status);
1405 }
1406 return status;
1407 }
Costin Manolache360e4542009-09-04 13:36:04 -07001408
Dianne Hackborn55280a92009-05-07 15:53:46 -07001409 public void writeAllState() {
1410 synchronized (mAuthorities) {
1411 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001412
Dianne Hackborn55280a92009-05-07 15:53:46 -07001413 if (mNumPendingFinished > 0) {
1414 // Only write these if they are out of date.
1415 writePendingOperationsLocked();
1416 }
Costin Manolache360e4542009-09-04 13:36:04 -07001417
Dianne Hackborn55280a92009-05-07 15:53:46 -07001418 // Just always write these... they are likely out of date.
1419 writeStatusLocked();
1420 writeStatisticsLocked();
1421 }
1422 }
Costin Manolache360e4542009-09-04 13:36:04 -07001423
Dianne Hackborn231cc602009-04-27 17:10:36 -07001424 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001425 * public for testing
1426 */
1427 public void clearAndReadState() {
1428 synchronized (mAuthorities) {
1429 mAuthorities.clear();
1430 mAccounts.clear();
1431 mPendingOperations.clear();
1432 mSyncStatus.clear();
1433 mSyncHistory.clear();
1434
1435 readAccountInfoLocked();
1436 readStatusLocked();
1437 readPendingOperationsLocked();
1438 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -07001439 readAndDeleteLegacyAccountInfoLocked();
1440 writeAccountInfoLocked();
1441 writeStatusLocked();
1442 writePendingOperationsLocked();
1443 writeStatisticsLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001444 }
1445 }
1446
1447 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001448 * Read all account information back in to the initial engine state.
1449 */
1450 private void readAccountInfoLocked() {
Fred Quintana77c560f2010-03-29 22:20:26 -07001451 int highestAuthorityId = -1;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001452 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001454 fis = mAccountInfoFile.openRead();
1455 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1456 XmlPullParser parser = Xml.newPullParser();
1457 parser.setInput(fis, null);
1458 int eventType = parser.getEventType();
1459 while (eventType != XmlPullParser.START_TAG) {
1460 eventType = parser.next();
1461 }
1462 String tagName = parser.getName();
1463 if ("accounts".equals(tagName)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001464 String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
Fred Quintanac2e46912010-03-15 16:10:44 -07001465 String versionString = parser.getAttributeValue(null, "version");
1466 int version;
1467 try {
1468 version = (versionString == null) ? 0 : Integer.parseInt(versionString);
1469 } catch (NumberFormatException e) {
1470 version = 0;
1471 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001472 String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
Fred Quintana77c560f2010-03-29 22:20:26 -07001473 try {
1474 int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
1475 mNextAuthorityId = Math.max(mNextAuthorityId, id);
1476 } catch (NumberFormatException e) {
1477 // don't care
Fred Quintanac2e46912010-03-15 16:10:44 -07001478 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07001479 String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET);
1480 try {
1481 mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString);
1482 } catch (NumberFormatException e) {
1483 mSyncRandomOffset = 0;
1484 }
1485 if (mSyncRandomOffset == 0) {
1486 Random random = new Random(System.currentTimeMillis());
1487 mSyncRandomOffset = random.nextInt(86400);
1488 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001489 mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001490 eventType = parser.next();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001491 AuthorityInfo authority = null;
1492 Pair<Bundle, Long> periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001493 do {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001494 if (eventType == XmlPullParser.START_TAG) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001495 tagName = parser.getName();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001496 if (parser.getDepth() == 2) {
1497 if ("authority".equals(tagName)) {
Fred Quintanac2e46912010-03-15 16:10:44 -07001498 authority = parseAuthority(parser, version);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001499 periodicSync = null;
Fred Quintana77c560f2010-03-29 22:20:26 -07001500 if (authority.ident > highestAuthorityId) {
1501 highestAuthorityId = authority.ident;
1502 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001503 } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) {
1504 parseListenForTickles(parser);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001505 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001506 } else if (parser.getDepth() == 3) {
1507 if ("periodicSync".equals(tagName) && authority != null) {
1508 periodicSync = parsePeriodicSync(parser, authority);
1509 }
1510 } else if (parser.getDepth() == 4 && periodicSync != null) {
1511 if ("extra".equals(tagName)) {
1512 parseExtra(parser, periodicSync);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001513 }
1514 }
1515 }
1516 eventType = parser.next();
1517 } while (eventType != XmlPullParser.END_DOCUMENT);
1518 }
1519 } catch (XmlPullParserException e) {
1520 Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001521 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001522 } catch (java.io.IOException e) {
1523 if (fis == null) Log.i(TAG, "No initial accounts");
1524 else Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001525 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001526 } finally {
Fred Quintana77c560f2010-03-29 22:20:26 -07001527 mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001528 if (fis != null) {
1529 try {
1530 fis.close();
1531 } catch (java.io.IOException e1) {
1532 }
1533 }
1534 }
Fred Quintanac2e46912010-03-15 16:10:44 -07001535
Fred Quintana77c560f2010-03-29 22:20:26 -07001536 maybeMigrateSettingsForRenamedAuthorities();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001537 }
Costin Manolache360e4542009-09-04 13:36:04 -07001538
Fred Quintanafb084402010-03-23 17:57:03 -07001539 /**
1540 * some authority names have changed. copy over their settings and delete the old ones
1541 * @return true if a change was made
1542 */
1543 private boolean maybeMigrateSettingsForRenamedAuthorities() {
1544 boolean writeNeeded = false;
1545
1546 ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
1547 final int N = mAuthorities.size();
1548 for (int i=0; i<N; i++) {
1549 AuthorityInfo authority = mAuthorities.valueAt(i);
1550 // skip this authority if it isn't one of the renamed ones
1551 final String newAuthorityName = sAuthorityRenames.get(authority.authority);
1552 if (newAuthorityName == null) {
1553 continue;
1554 }
1555
1556 // remember this authority so we can remove it later. we can't remove it
1557 // now without messing up this loop iteration
1558 authoritiesToRemove.add(authority);
1559
1560 // this authority isn't enabled, no need to copy it to the new authority name since
1561 // the default is "disabled"
1562 if (!authority.enabled) {
1563 continue;
1564 }
1565
1566 // if we already have a record of this new authority then don't copy over the settings
Amith Yamasani04e0d262012-02-14 11:50:53 -08001567 if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup")
1568 != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001569 continue;
1570 }
1571
1572 AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001573 authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001574 newAuthority.enabled = true;
1575 writeNeeded = true;
1576 }
1577
1578 for (AuthorityInfo authorityInfo : authoritiesToRemove) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001579 removeAuthorityLocked(authorityInfo.account, authorityInfo.userId,
1580 authorityInfo.authority, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001581 writeNeeded = true;
1582 }
1583
1584 return writeNeeded;
1585 }
1586
Amith Yamasani04e0d262012-02-14 11:50:53 -08001587 private void parseListenForTickles(XmlPullParser parser) {
1588 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1589 int userId = 0;
1590 try {
1591 userId = Integer.parseInt(user);
1592 } catch (NumberFormatException e) {
1593 Log.e(TAG, "error parsing the user for listen-for-tickles", e);
1594 } catch (NullPointerException e) {
1595 Log.e(TAG, "the user in listen-for-tickles is null", e);
1596 }
1597 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
1598 boolean listen = enabled == null || Boolean.parseBoolean(enabled);
1599 mMasterSyncAutomatically.put(userId, listen);
1600 }
1601
Fred Quintanac2e46912010-03-15 16:10:44 -07001602 private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001603 AuthorityInfo authority = null;
1604 int id = -1;
1605 try {
1606 id = Integer.parseInt(parser.getAttributeValue(
1607 null, "id"));
1608 } catch (NumberFormatException e) {
1609 Log.e(TAG, "error parsing the id of the authority", e);
1610 } catch (NullPointerException e) {
1611 Log.e(TAG, "the id of the authority is null", e);
1612 }
1613 if (id >= 0) {
Fred Quintanafb084402010-03-23 17:57:03 -07001614 String authorityName = parser.getAttributeValue(null, "authority");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001615 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
Fred Quintanafb084402010-03-23 17:57:03 -07001616 String syncable = parser.getAttributeValue(null, "syncable");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001617 String accountName = parser.getAttributeValue(null, "account");
1618 String accountType = parser.getAttributeValue(null, "type");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001619 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1620 int userId = user == null ? 0 : Integer.parseInt(user);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001621 if (accountType == null) {
1622 accountType = "com.google";
Fred Quintanafb084402010-03-23 17:57:03 -07001623 syncable = "unknown";
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001624 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001625 authority = mAuthorities.get(id);
1626 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1627 + accountName + " auth=" + authorityName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001628 + " user=" + userId
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001629 + " enabled=" + enabled
1630 + " syncable=" + syncable);
1631 if (authority == null) {
1632 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1633 authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001634 new Account(accountName, accountType), userId, authorityName, id, false);
Fred Quintanac2e46912010-03-15 16:10:44 -07001635 // If the version is 0 then we are upgrading from a file format that did not
1636 // know about periodic syncs. In that case don't clear the list since we
1637 // want the default, which is a daily periodioc sync.
1638 // Otherwise clear out this default list since we will populate it later with
1639 // the periodic sync descriptions that are read from the configuration file.
1640 if (version > 0) {
1641 authority.periodicSyncs.clear();
1642 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001643 }
1644 if (authority != null) {
1645 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
1646 if ("unknown".equals(syncable)) {
1647 authority.syncable = -1;
1648 } else {
1649 authority.syncable =
Fred Quintanafb084402010-03-23 17:57:03 -07001650 (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001651 }
1652 } else {
1653 Log.w(TAG, "Failure adding authority: account="
1654 + accountName + " auth=" + authorityName
1655 + " enabled=" + enabled
1656 + " syncable=" + syncable);
1657 }
1658 }
1659
1660 return authority;
1661 }
1662
1663 private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
1664 Bundle extras = new Bundle();
1665 String periodValue = parser.getAttributeValue(null, "period");
1666 final long period;
1667 try {
1668 period = Long.parseLong(periodValue);
1669 } catch (NumberFormatException e) {
1670 Log.e(TAG, "error parsing the period of a periodic sync", e);
1671 return null;
1672 } catch (NullPointerException e) {
1673 Log.e(TAG, "the period of a periodic sync is null", e);
1674 return null;
1675 }
1676 final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
1677 authority.periodicSyncs.add(periodicSync);
Fred Quintanac2e46912010-03-15 16:10:44 -07001678
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001679 return periodicSync;
1680 }
1681
1682 private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
1683 final Bundle extras = periodicSync.first;
1684 String name = parser.getAttributeValue(null, "name");
1685 String type = parser.getAttributeValue(null, "type");
1686 String value1 = parser.getAttributeValue(null, "value1");
1687 String value2 = parser.getAttributeValue(null, "value2");
1688
1689 try {
1690 if ("long".equals(type)) {
1691 extras.putLong(name, Long.parseLong(value1));
1692 } else if ("integer".equals(type)) {
1693 extras.putInt(name, Integer.parseInt(value1));
1694 } else if ("double".equals(type)) {
1695 extras.putDouble(name, Double.parseDouble(value1));
1696 } else if ("float".equals(type)) {
1697 extras.putFloat(name, Float.parseFloat(value1));
1698 } else if ("boolean".equals(type)) {
1699 extras.putBoolean(name, Boolean.parseBoolean(value1));
1700 } else if ("string".equals(type)) {
1701 extras.putString(name, value1);
1702 } else if ("account".equals(type)) {
1703 extras.putParcelable(name, new Account(value1, value2));
1704 }
1705 } catch (NumberFormatException e) {
1706 Log.e(TAG, "error parsing bundle value", e);
1707 } catch (NullPointerException e) {
1708 Log.e(TAG, "error parsing bundle value", e);
1709 }
1710 }
1711
Dianne Hackborn231cc602009-04-27 17:10:36 -07001712 /**
1713 * Write all account information to the account file.
1714 */
1715 private void writeAccountInfoLocked() {
1716 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1717 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001718
Dianne Hackborn231cc602009-04-27 17:10:36 -07001719 try {
1720 fos = mAccountInfoFile.startWrite();
1721 XmlSerializer out = new FastXmlSerializer();
1722 out.setOutput(fos, "utf-8");
1723 out.startDocument(null, true);
1724 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001725
Dianne Hackborn231cc602009-04-27 17:10:36 -07001726 out.startTag(null, "accounts");
Fred Quintanac2e46912010-03-15 16:10:44 -07001727 out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001728 out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
Ashish Sharma69d95de2012-04-11 17:27:24 -07001729 out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001730
1731 // Write the Sync Automatically flags for each user
1732 final int M = mMasterSyncAutomatically.size();
1733 for (int m = 0; m < M; m++) {
1734 int userId = mMasterSyncAutomatically.keyAt(m);
1735 Boolean listen = mMasterSyncAutomatically.valueAt(m);
1736 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
1737 out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
1738 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
1739 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001740 }
Costin Manolache360e4542009-09-04 13:36:04 -07001741
Dianne Hackborn231cc602009-04-27 17:10:36 -07001742 final int N = mAuthorities.size();
1743 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001744 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001745 out.startTag(null, "authority");
1746 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001747 out.attribute(null, "account", authority.account.name);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001748 out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001749 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001750 out.attribute(null, "authority", authority.authority);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001751 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
Fred Quintana5e787c42009-08-16 23:13:53 -07001752 if (authority.syncable < 0) {
1753 out.attribute(null, "syncable", "unknown");
Fred Quintanafb084402010-03-23 17:57:03 -07001754 } else {
1755 out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
Fred Quintana5e787c42009-08-16 23:13:53 -07001756 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001757 for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
1758 out.startTag(null, "periodicSync");
1759 out.attribute(null, "period", Long.toString(periodicSync.second));
1760 final Bundle extras = periodicSync.first;
1761 for (String key : extras.keySet()) {
1762 out.startTag(null, "extra");
1763 out.attribute(null, "name", key);
1764 final Object value = extras.get(key);
1765 if (value instanceof Long) {
1766 out.attribute(null, "type", "long");
1767 out.attribute(null, "value1", value.toString());
1768 } else if (value instanceof Integer) {
1769 out.attribute(null, "type", "integer");
1770 out.attribute(null, "value1", value.toString());
1771 } else if (value instanceof Boolean) {
1772 out.attribute(null, "type", "boolean");
1773 out.attribute(null, "value1", value.toString());
1774 } else if (value instanceof Float) {
1775 out.attribute(null, "type", "float");
1776 out.attribute(null, "value1", value.toString());
1777 } else if (value instanceof Double) {
1778 out.attribute(null, "type", "double");
1779 out.attribute(null, "value1", value.toString());
1780 } else if (value instanceof String) {
1781 out.attribute(null, "type", "string");
1782 out.attribute(null, "value1", value.toString());
1783 } else if (value instanceof Account) {
1784 out.attribute(null, "type", "account");
1785 out.attribute(null, "value1", ((Account)value).name);
1786 out.attribute(null, "value2", ((Account)value).type);
1787 }
1788 out.endTag(null, "extra");
1789 }
1790 out.endTag(null, "periodicSync");
1791 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001792 out.endTag(null, "authority");
1793 }
Costin Manolache360e4542009-09-04 13:36:04 -07001794
Dianne Hackborn231cc602009-04-27 17:10:36 -07001795 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001796
Dianne Hackborn231cc602009-04-27 17:10:36 -07001797 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001798
Dianne Hackborn231cc602009-04-27 17:10:36 -07001799 mAccountInfoFile.finishWrite(fos);
1800 } catch (java.io.IOException e1) {
1801 Log.w(TAG, "Error writing accounts", e1);
1802 if (fos != null) {
1803 mAccountInfoFile.failWrite(fos);
1804 }
1805 }
1806 }
Costin Manolache360e4542009-09-04 13:36:04 -07001807
Dianne Hackborn231cc602009-04-27 17:10:36 -07001808 static int getIntColumn(Cursor c, String name) {
1809 return c.getInt(c.getColumnIndex(name));
1810 }
Costin Manolache360e4542009-09-04 13:36:04 -07001811
Dianne Hackborn231cc602009-04-27 17:10:36 -07001812 static long getLongColumn(Cursor c, String name) {
1813 return c.getLong(c.getColumnIndex(name));
1814 }
Costin Manolache360e4542009-09-04 13:36:04 -07001815
Dianne Hackborn231cc602009-04-27 17:10:36 -07001816 /**
1817 * Load sync engine state from the old syncmanager database, and then
1818 * erase it. Note that we don't deal with pending operations, active
1819 * sync, or history.
1820 */
Fred Quintana77c560f2010-03-29 22:20:26 -07001821 private void readAndDeleteLegacyAccountInfoLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001822 // Look for old database to initialize from.
1823 File file = mContext.getDatabasePath("syncmanager.db");
1824 if (!file.exists()) {
1825 return;
1826 }
1827 String path = file.getPath();
1828 SQLiteDatabase db = null;
1829 try {
1830 db = SQLiteDatabase.openDatabase(path, null,
1831 SQLiteDatabase.OPEN_READONLY);
1832 } catch (SQLiteException e) {
1833 }
Costin Manolache360e4542009-09-04 13:36:04 -07001834
Dianne Hackborn231cc602009-04-27 17:10:36 -07001835 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001836 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001837
Dianne Hackborn231cc602009-04-27 17:10:36 -07001838 // Copy in all of the status information, as well as accounts.
1839 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1840 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1841 qb.setTables("stats, status");
1842 HashMap<String,String> map = new HashMap<String,String>();
1843 map.put("_id", "status._id as _id");
1844 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001845 if (hasType) {
1846 map.put("account_type", "stats.account_type as account_type");
1847 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001848 map.put("authority", "stats.authority as authority");
1849 map.put("totalElapsedTime", "totalElapsedTime");
1850 map.put("numSyncs", "numSyncs");
1851 map.put("numSourceLocal", "numSourceLocal");
1852 map.put("numSourcePoll", "numSourcePoll");
1853 map.put("numSourceServer", "numSourceServer");
1854 map.put("numSourceUser", "numSourceUser");
1855 map.put("lastSuccessSource", "lastSuccessSource");
1856 map.put("lastSuccessTime", "lastSuccessTime");
1857 map.put("lastFailureSource", "lastFailureSource");
1858 map.put("lastFailureTime", "lastFailureTime");
1859 map.put("lastFailureMesg", "lastFailureMesg");
1860 map.put("pending", "pending");
1861 qb.setProjectionMap(map);
1862 qb.appendWhere("stats._id = status.stats_id");
1863 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001865 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001866 String accountType = hasType
1867 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001868 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001869 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001871 String authorityName = c.getString(c.getColumnIndex("authority"));
1872 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001873 new Account(accountName, accountType), 0 /* legacy is single-user */,
Dianne Hackborn7a135592009-05-06 00:28:37 -07001874 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001875 if (authority != null) {
1876 int i = mSyncStatus.size();
1877 boolean found = false;
1878 SyncStatusInfo st = null;
1879 while (i > 0) {
1880 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001881 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001882 if (st.authorityId == authority.ident) {
1883 found = true;
1884 break;
1885 }
1886 }
1887 if (!found) {
1888 st = new SyncStatusInfo(authority.ident);
1889 mSyncStatus.put(authority.ident, st);
1890 }
1891 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1892 st.numSyncs = getIntColumn(c, "numSyncs");
1893 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1894 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1895 st.numSourceServer = getIntColumn(c, "numSourceServer");
1896 st.numSourceUser = getIntColumn(c, "numSourceUser");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001897 st.numSourcePeriodic = 0;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001898 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1899 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1900 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1901 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1902 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1903 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 }
Costin Manolache360e4542009-09-04 13:36:04 -07001906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001908
Dianne Hackborn231cc602009-04-27 17:10:36 -07001909 // Retrieve the settings.
1910 qb = new SQLiteQueryBuilder();
1911 qb.setTables("settings");
1912 c = qb.query(db, null, null, null, null, null, null);
1913 while (c.moveToNext()) {
1914 String name = c.getString(c.getColumnIndex("name"));
1915 String value = c.getString(c.getColumnIndex("value"));
1916 if (name == null) continue;
1917 if (name.equals("listen_for_tickles")) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001918 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001919 } else if (name.startsWith("sync_provider_")) {
1920 String provider = name.substring("sync_provider_".length(),
1921 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001922 int i = mAuthorities.size();
1923 while (i > 0) {
1924 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001925 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001926 if (authority.authority.equals(provider)) {
1927 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001928 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001929 }
1930 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001931 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 }
Costin Manolache360e4542009-09-04 13:36:04 -07001933
Dianne Hackborn231cc602009-04-27 17:10:36 -07001934 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001935
Dianne Hackborn231cc602009-04-27 17:10:36 -07001936 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001937
Dianne Hackborn231cc602009-04-27 17:10:36 -07001938 (new File(path)).delete();
1939 }
1940 }
Costin Manolache360e4542009-09-04 13:36:04 -07001941
Dianne Hackborn231cc602009-04-27 17:10:36 -07001942 public static final int STATUS_FILE_END = 0;
1943 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001944
Dianne Hackborn231cc602009-04-27 17:10:36 -07001945 /**
1946 * Read all sync status back in to the initial engine state.
1947 */
1948 private void readStatusLocked() {
1949 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1950 try {
1951 byte[] data = mStatusFile.readFully();
1952 Parcel in = Parcel.obtain();
1953 in.unmarshall(data, 0, data.length);
1954 in.setDataPosition(0);
1955 int token;
1956 while ((token=in.readInt()) != STATUS_FILE_END) {
1957 if (token == STATUS_FILE_ITEM) {
1958 SyncStatusInfo status = new SyncStatusInfo(in);
1959 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1960 status.pending = false;
1961 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1962 + status.authorityId);
1963 mSyncStatus.put(status.authorityId, status);
1964 }
1965 } else {
1966 // Ooops.
1967 Log.w(TAG, "Unknown status token: " + token);
1968 break;
1969 }
1970 }
1971 } catch (java.io.IOException e) {
1972 Log.i(TAG, "No initial status");
1973 }
1974 }
Costin Manolache360e4542009-09-04 13:36:04 -07001975
Dianne Hackborn231cc602009-04-27 17:10:36 -07001976 /**
1977 * Write all sync status to the sync status file.
1978 */
1979 private void writeStatusLocked() {
1980 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001981
Dianne Hackborn231cc602009-04-27 17:10:36 -07001982 // The file is being written, so we don't need to have a scheduled
1983 // write until the next change.
1984 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001985
Dianne Hackborn231cc602009-04-27 17:10:36 -07001986 FileOutputStream fos = null;
1987 try {
1988 fos = mStatusFile.startWrite();
1989 Parcel out = Parcel.obtain();
1990 final int N = mSyncStatus.size();
1991 for (int i=0; i<N; i++) {
1992 SyncStatusInfo status = mSyncStatus.valueAt(i);
1993 out.writeInt(STATUS_FILE_ITEM);
1994 status.writeToParcel(out, 0);
1995 }
1996 out.writeInt(STATUS_FILE_END);
1997 fos.write(out.marshall());
1998 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001999
Dianne Hackborn231cc602009-04-27 17:10:36 -07002000 mStatusFile.finishWrite(fos);
2001 } catch (java.io.IOException e1) {
2002 Log.w(TAG, "Error writing status", e1);
2003 if (fos != null) {
2004 mStatusFile.failWrite(fos);
2005 }
2006 }
2007 }
Costin Manolache360e4542009-09-04 13:36:04 -07002008
Fred Quintana307da1a2010-01-21 14:24:20 -08002009 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07002010
Dianne Hackborn231cc602009-04-27 17:10:36 -07002011 /**
2012 * Read all pending operations back in to the initial engine state.
2013 */
2014 private void readPendingOperationsLocked() {
2015 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
2016 try {
2017 byte[] data = mPendingFile.readFully();
2018 Parcel in = Parcel.obtain();
2019 in.unmarshall(data, 0, data.length);
2020 in.setDataPosition(0);
2021 final int SIZE = in.dataSize();
2022 while (in.dataPosition() < SIZE) {
2023 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08002024 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002025 Log.w(TAG, "Unknown pending operation version "
2026 + version + "; dropping all ops");
2027 break;
2028 }
2029 int authorityId = in.readInt();
2030 int syncSource = in.readInt();
2031 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08002032 boolean expedited;
2033 if (version == PENDING_OPERATION_VERSION) {
2034 expedited = in.readInt() != 0;
2035 } else {
2036 expedited = false;
2037 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002038 AuthorityInfo authority = mAuthorities.get(authorityId);
2039 if (authority != null) {
Fred Quintana5695c7b2010-12-06 15:07:52 -08002040 Bundle extras;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002041 if (flatExtras != null) {
2042 extras = unflattenBundle(flatExtras);
Fred Quintana5695c7b2010-12-06 15:07:52 -08002043 } else {
2044 // if we are unable to parse the extras for whatever reason convert this
2045 // to a regular sync by creating an empty extras
2046 extras = new Bundle();
Dianne Hackborn231cc602009-04-27 17:10:36 -07002047 }
2048 PendingOperation op = new PendingOperation(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002049 authority.account, authority.userId, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08002050 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002051 op.authorityId = authorityId;
2052 op.flatExtras = flatExtras;
2053 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
2054 + " auth=" + op.authority
2055 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08002056 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07002057 + " extras=" + op.extras);
2058 mPendingOperations.add(op);
2059 }
2060 }
2061 } catch (java.io.IOException e) {
2062 Log.i(TAG, "No initial pending operations");
2063 }
2064 }
Costin Manolache360e4542009-09-04 13:36:04 -07002065
Dianne Hackborn231cc602009-04-27 17:10:36 -07002066 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
2067 out.writeInt(PENDING_OPERATION_VERSION);
2068 out.writeInt(op.authorityId);
2069 out.writeInt(op.syncSource);
2070 if (op.flatExtras == null && op.extras != null) {
2071 op.flatExtras = flattenBundle(op.extras);
2072 }
2073 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08002074 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002075 }
Costin Manolache360e4542009-09-04 13:36:04 -07002076
Dianne Hackborn231cc602009-04-27 17:10:36 -07002077 /**
2078 * Write all currently pending ops to the pending ops file.
2079 */
2080 private void writePendingOperationsLocked() {
2081 final int N = mPendingOperations.size();
2082 FileOutputStream fos = null;
2083 try {
2084 if (N == 0) {
2085 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
2086 mPendingFile.truncate();
2087 return;
2088 }
Costin Manolache360e4542009-09-04 13:36:04 -07002089
Dianne Hackborn231cc602009-04-27 17:10:36 -07002090 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
2091 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07002092
Dianne Hackborn231cc602009-04-27 17:10:36 -07002093 Parcel out = Parcel.obtain();
2094 for (int i=0; i<N; i++) {
2095 PendingOperation op = mPendingOperations.get(i);
2096 writePendingOperationLocked(op, out);
2097 }
2098 fos.write(out.marshall());
2099 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002100
Dianne Hackborn231cc602009-04-27 17:10:36 -07002101 mPendingFile.finishWrite(fos);
2102 } catch (java.io.IOException e1) {
2103 Log.w(TAG, "Error writing pending operations", e1);
2104 if (fos != null) {
2105 mPendingFile.failWrite(fos);
2106 }
2107 }
2108 }
Costin Manolache360e4542009-09-04 13:36:04 -07002109
Dianne Hackborn231cc602009-04-27 17:10:36 -07002110 /**
2111 * Append the given operation to the pending ops file; if unable to,
2112 * write all pending ops.
2113 */
2114 private void appendPendingOperationLocked(PendingOperation op) {
2115 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
2116 FileOutputStream fos = null;
2117 try {
2118 fos = mPendingFile.openAppend();
2119 } catch (java.io.IOException e) {
2120 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
2121 writePendingOperationsLocked();
2122 return;
2123 }
Costin Manolache360e4542009-09-04 13:36:04 -07002124
Dianne Hackborn231cc602009-04-27 17:10:36 -07002125 try {
2126 Parcel out = Parcel.obtain();
2127 writePendingOperationLocked(op, out);
2128 fos.write(out.marshall());
2129 out.recycle();
2130 } catch (java.io.IOException e1) {
2131 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002133 try {
2134 fos.close();
2135 } catch (java.io.IOException e2) {
2136 }
2137 }
2138 }
Costin Manolache360e4542009-09-04 13:36:04 -07002139
Dianne Hackborn231cc602009-04-27 17:10:36 -07002140 static private byte[] flattenBundle(Bundle bundle) {
2141 byte[] flatData = null;
2142 Parcel parcel = Parcel.obtain();
2143 try {
2144 bundle.writeToParcel(parcel, 0);
2145 flatData = parcel.marshall();
2146 } finally {
2147 parcel.recycle();
2148 }
2149 return flatData;
2150 }
Costin Manolache360e4542009-09-04 13:36:04 -07002151
Dianne Hackborn231cc602009-04-27 17:10:36 -07002152 static private Bundle unflattenBundle(byte[] flatData) {
2153 Bundle bundle;
2154 Parcel parcel = Parcel.obtain();
2155 try {
2156 parcel.unmarshall(flatData, 0, flatData.length);
2157 parcel.setDataPosition(0);
2158 bundle = parcel.readBundle();
2159 } catch (RuntimeException e) {
2160 // A RuntimeException is thrown if we were unable to parse the parcel.
2161 // Create an empty parcel in this case.
2162 bundle = new Bundle();
2163 } finally {
2164 parcel.recycle();
2165 }
2166 return bundle;
2167 }
Costin Manolache360e4542009-09-04 13:36:04 -07002168
Amith Yamasani04e0d262012-02-14 11:50:53 -08002169 private void requestSync(Account account, int userId, String authority, Bundle extras) {
2170 // If this is happening in the system process, then call the syncrequest listener
2171 // to make a request back to the SyncManager directly.
2172 // If this is probably a test instance, then call back through the ContentResolver
2173 // which will know which userId to apply based on the Binder id.
2174 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
2175 && mSyncRequestListener != null) {
2176 mSyncRequestListener.onSyncRequest(account, userId, authority, extras);
2177 } else {
2178 ContentResolver.requestSync(account, authority, extras);
2179 }
2180 }
2181
Dianne Hackborn231cc602009-04-27 17:10:36 -07002182 public static final int STATISTICS_FILE_END = 0;
2183 public static final int STATISTICS_FILE_ITEM_OLD = 100;
2184 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07002185
Dianne Hackborn231cc602009-04-27 17:10:36 -07002186 /**
2187 * Read all sync statistics back in to the initial engine state.
2188 */
2189 private void readStatisticsLocked() {
2190 try {
2191 byte[] data = mStatisticsFile.readFully();
2192 Parcel in = Parcel.obtain();
2193 in.unmarshall(data, 0, data.length);
2194 in.setDataPosition(0);
2195 int token;
2196 int index = 0;
2197 while ((token=in.readInt()) != STATISTICS_FILE_END) {
2198 if (token == STATISTICS_FILE_ITEM
2199 || token == STATISTICS_FILE_ITEM_OLD) {
2200 int day = in.readInt();
2201 if (token == STATISTICS_FILE_ITEM_OLD) {
2202 day = day - 2009 + 14245; // Magic!
2203 }
2204 DayStats ds = new DayStats(day);
2205 ds.successCount = in.readInt();
2206 ds.successTime = in.readLong();
2207 ds.failureCount = in.readInt();
2208 ds.failureTime = in.readLong();
2209 if (index < mDayStats.length) {
2210 mDayStats[index] = ds;
2211 index++;
2212 }
2213 } else {
2214 // Ooops.
2215 Log.w(TAG, "Unknown stats token: " + token);
2216 break;
2217 }
2218 }
2219 } catch (java.io.IOException e) {
2220 Log.i(TAG, "No initial statistics");
2221 }
2222 }
Costin Manolache360e4542009-09-04 13:36:04 -07002223
Dianne Hackborn231cc602009-04-27 17:10:36 -07002224 /**
2225 * Write all sync statistics to the sync status file.
2226 */
2227 private void writeStatisticsLocked() {
2228 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07002229
Dianne Hackborn231cc602009-04-27 17:10:36 -07002230 // The file is being written, so we don't need to have a scheduled
2231 // write until the next change.
2232 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07002233
Dianne Hackborn231cc602009-04-27 17:10:36 -07002234 FileOutputStream fos = null;
2235 try {
2236 fos = mStatisticsFile.startWrite();
2237 Parcel out = Parcel.obtain();
2238 final int N = mDayStats.length;
2239 for (int i=0; i<N; i++) {
2240 DayStats ds = mDayStats[i];
2241 if (ds == null) {
2242 break;
2243 }
2244 out.writeInt(STATISTICS_FILE_ITEM);
2245 out.writeInt(ds.day);
2246 out.writeInt(ds.successCount);
2247 out.writeLong(ds.successTime);
2248 out.writeInt(ds.failureCount);
2249 out.writeLong(ds.failureTime);
2250 }
2251 out.writeInt(STATISTICS_FILE_END);
2252 fos.write(out.marshall());
2253 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002254
Dianne Hackborn231cc602009-04-27 17:10:36 -07002255 mStatisticsFile.finishWrite(fos);
2256 } catch (java.io.IOException e1) {
2257 Log.w(TAG, "Error writing stats", e1);
2258 if (fos != null) {
2259 mStatisticsFile.failWrite(fos);
2260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002261 }
2262 }
2263}