blob: 5ab9e5a3ec501b48a77635e91d6de009c4ef1aa4 [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 Yamasani04e0d262012-02-14 11:50:53 -080028import android.content.SyncManager.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;
224 }
Costin Manolache360e4542009-09-04 13:36:04 -0700225
Dianne Hackborn231cc602009-04-27 17:10:36 -0700226 public static class DayStats {
227 public final int day;
228 public int successCount;
229 public long successTime;
230 public int failureCount;
231 public long failureTime;
Costin Manolache360e4542009-09-04 13:36:04 -0700232
Dianne Hackborn231cc602009-04-27 17:10:36 -0700233 public DayStats(int day) {
234 this.day = day;
235 }
236 }
Costin Manolache360e4542009-09-04 13:36:04 -0700237
Amith Yamasani04e0d262012-02-14 11:50:53 -0800238 interface OnSyncRequestListener {
239 /**
240 * Called when a sync is needed on an account(s) due to some change in state.
241 * @param account
242 * @param userId
243 * @param authority
244 * @param extras
245 */
246 public void onSyncRequest(Account account, int userId, String authority, Bundle extras);
247 }
248
Dianne Hackborn231cc602009-04-27 17:10:36 -0700249 // Primary list of all syncable authorities. Also our global lock.
250 private final SparseArray<AuthorityInfo> mAuthorities =
251 new SparseArray<AuthorityInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700252
Amith Yamasani04e0d262012-02-14 11:50:53 -0800253 private final HashMap<AccountAndUser, AccountInfo> mAccounts
254 = new HashMap<AccountAndUser, AccountInfo>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255
Dianne Hackborn231cc602009-04-27 17:10:36 -0700256 private final ArrayList<PendingOperation> mPendingOperations =
257 new ArrayList<PendingOperation>();
Costin Manolache360e4542009-09-04 13:36:04 -0700258
Amith Yamasani04e0d262012-02-14 11:50:53 -0800259 private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs
260 = new SparseArray<ArrayList<SyncInfo>>();
Costin Manolache360e4542009-09-04 13:36:04 -0700261
Dianne Hackborn231cc602009-04-27 17:10:36 -0700262 private final SparseArray<SyncStatusInfo> mSyncStatus =
263 new SparseArray<SyncStatusInfo>();
Costin Manolache360e4542009-09-04 13:36:04 -0700264
Dianne Hackborn231cc602009-04-27 17:10:36 -0700265 private final ArrayList<SyncHistoryItem> mSyncHistory =
266 new ArrayList<SyncHistoryItem>();
Costin Manolache360e4542009-09-04 13:36:04 -0700267
Dianne Hackborn231cc602009-04-27 17:10:36 -0700268 private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
269 = new RemoteCallbackList<ISyncStatusObserver>();
Costin Manolache360e4542009-09-04 13:36:04 -0700270
Fred Quintana77c560f2010-03-29 22:20:26 -0700271 private int mNextAuthorityId = 0;
272
Dianne Hackborn231cc602009-04-27 17:10:36 -0700273 // We keep 4 weeks of stats.
274 private final DayStats[] mDayStats = new DayStats[7*4];
275 private final Calendar mCal;
276 private int mYear;
277 private int mYearInDays;
Costin Manolache360e4542009-09-04 13:36:04 -0700278
Dianne Hackborn231cc602009-04-27 17:10:36 -0700279 private final Context mContext;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800280
Dianne Hackborn231cc602009-04-27 17:10:36 -0700281 private static volatile SyncStorageEngine sSyncStorageEngine = null;
Costin Manolache360e4542009-09-04 13:36:04 -0700282
Ashish Sharma69d95de2012-04-11 17:27:24 -0700283 private int mSyncRandomOffset;
284
Dianne Hackborn231cc602009-04-27 17:10:36 -0700285 /**
286 * This file contains the core engine state: all accounts and the
287 * settings for them. It must never be lost, and should be changed
288 * infrequently, so it is stored as an XML file.
289 */
290 private final AtomicFile mAccountInfoFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700291
Dianne Hackborn231cc602009-04-27 17:10:36 -0700292 /**
293 * This file contains the current sync status. We would like to retain
294 * it across boots, but its loss is not the end of the world, so we store
295 * this information as binary data.
296 */
297 private final AtomicFile mStatusFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700298
Dianne Hackborn231cc602009-04-27 17:10:36 -0700299 /**
300 * This file contains sync statistics. This is purely debugging information
301 * so is written infrequently and can be thrown away at any time.
302 */
303 private final AtomicFile mStatisticsFile;
Costin Manolache360e4542009-09-04 13:36:04 -0700304
Dianne Hackborn231cc602009-04-27 17:10:36 -0700305 /**
306 * This file contains the pending sync operations. It is a binary file,
307 * which must be updated every time an operation is added or removed,
308 * so we have special handling of it.
309 */
310 private final AtomicFile mPendingFile;
311 private static final int PENDING_FINISH_TO_WRITE = 4;
312 private int mNumPendingFinished = 0;
Costin Manolache360e4542009-09-04 13:36:04 -0700313
Dianne Hackborn231cc602009-04-27 17:10:36 -0700314 private int mNextHistoryId = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800315 private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
316
317 private OnSyncRequestListener mSyncRequestListener;
Costin Manolache360e4542009-09-04 13:36:04 -0700318
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800319 private SyncStorageEngine(Context context, File dataDir) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 mContext = context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 sSyncStorageEngine = this;
Costin Manolache360e4542009-09-04 13:36:04 -0700322
Dianne Hackborn231cc602009-04-27 17:10:36 -0700323 mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
Costin Manolache360e4542009-09-04 13:36:04 -0700324
Dianne Hackborn231cc602009-04-27 17:10:36 -0700325 File systemDir = new File(dataDir, "system");
326 File syncDir = new File(systemDir, "sync");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800327 syncDir.mkdirs();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700328 mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
329 mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
330 mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
331 mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
Costin Manolache360e4542009-09-04 13:36:04 -0700332
Dianne Hackborn231cc602009-04-27 17:10:36 -0700333 readAccountInfoLocked();
334 readStatusLocked();
335 readPendingOperationsLocked();
336 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -0700337 readAndDeleteLegacyAccountInfoLocked();
338 writeAccountInfoLocked();
339 writeStatusLocked();
340 writePendingOperationsLocked();
341 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343
344 public static SyncStorageEngine newTestInstance(Context context) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800345 return new SyncStorageEngine(context, context.getFilesDir());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
347
348 public static void init(Context context) {
349 if (sSyncStorageEngine != null) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800350 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800352 // This call will return the correct directory whether Encrypted File Systems is
353 // enabled or not.
Jason parksa3cdaa52011-01-13 14:15:43 -0600354 File dataDir = Environment.getSecureDataDirectory();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800355 sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 }
357
358 public static SyncStorageEngine getSingleton() {
359 if (sSyncStorageEngine == null) {
360 throw new IllegalStateException("not initialized");
361 }
362 return sSyncStorageEngine;
363 }
364
Amith Yamasani04e0d262012-02-14 11:50:53 -0800365 protected void setOnSyncRequestListener(OnSyncRequestListener listener) {
366 if (mSyncRequestListener == null) {
367 mSyncRequestListener = listener;
368 }
369 }
370
Dianne Hackborn231cc602009-04-27 17:10:36 -0700371 @Override public void handleMessage(Message msg) {
372 if (msg.what == MSG_WRITE_STATUS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700373 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700374 writeStatusLocked();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700375 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700376 } else if (msg.what == MSG_WRITE_STATISTICS) {
Dianne Hackborn4e808202010-04-06 22:00:59 -0700377 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700378 writeStatisticsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 }
380 }
381 }
Costin Manolache360e4542009-09-04 13:36:04 -0700382
Ashish Sharma69d95de2012-04-11 17:27:24 -0700383 public int getSyncRandomOffset() {
384 return mSyncRandomOffset;
385 }
386
Dianne Hackborn231cc602009-04-27 17:10:36 -0700387 public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
388 synchronized (mAuthorities) {
389 mChangeListeners.register(callback, mask);
390 }
391 }
Costin Manolache360e4542009-09-04 13:36:04 -0700392
Dianne Hackborn231cc602009-04-27 17:10:36 -0700393 public void removeStatusChangeListener(ISyncStatusObserver callback) {
394 synchronized (mAuthorities) {
395 mChangeListeners.unregister(callback);
396 }
397 }
Costin Manolache360e4542009-09-04 13:36:04 -0700398
Dianne Hackborn231cc602009-04-27 17:10:36 -0700399 private void reportChange(int which) {
400 ArrayList<ISyncStatusObserver> reports = null;
401 synchronized (mAuthorities) {
402 int i = mChangeListeners.beginBroadcast();
403 while (i > 0) {
404 i--;
405 Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
406 if ((which & mask.intValue()) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 continue;
408 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700409 if (reports == null) {
410 reports = new ArrayList<ISyncStatusObserver>(i);
411 }
412 reports.add(mChangeListeners.getBroadcastItem(i));
413 }
Dianne Hackbornb06ea702009-07-13 13:07:51 -0700414 mChangeListeners.finishBroadcast();
Dianne Hackborn231cc602009-04-27 17:10:36 -0700415 }
Costin Manolache360e4542009-09-04 13:36:04 -0700416
Fred Quintana77c560f2010-03-29 22:20:26 -0700417 if (Log.isLoggable(TAG, Log.VERBOSE)) {
418 Log.v(TAG, "reportChange " + which + " to: " + reports);
419 }
Costin Manolache360e4542009-09-04 13:36:04 -0700420
Dianne Hackborn231cc602009-04-27 17:10:36 -0700421 if (reports != null) {
422 int i = reports.size();
423 while (i > 0) {
424 i--;
425 try {
426 reports.get(i).onStatusChanged(which);
427 } catch (RemoteException e) {
428 // The remote callback list will take care of this for us.
429 }
430 }
431 }
432 }
Amith Yamasani70c874b2009-07-06 14:53:25 -0700433
Amith Yamasani04e0d262012-02-14 11:50:53 -0800434 public boolean getSyncAutomatically(Account account, int userId, String providerName) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700435 synchronized (mAuthorities) {
436 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800437 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintanaac9385e2009-06-22 18:00:59 -0700438 "getSyncAutomatically");
439 return authority != null && authority.enabled;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700440 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700441
Dianne Hackborn231cc602009-04-27 17:10:36 -0700442 int i = mAuthorities.size();
443 while (i > 0) {
444 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700445 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700446 if (authority.authority.equals(providerName)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800447 && authority.userId == userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700448 && authority.enabled) {
449 return true;
450 }
451 }
452 return false;
453 }
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455
Amith Yamasani04e0d262012-02-14 11:50:53 -0800456 public void setSyncAutomatically(Account account, int userId, String providerName,
457 boolean sync) {
458 Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
459 + ", user " + userId + " -> " + sync);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700460 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800461 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
462 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700463 if (authority.enabled == sync) {
464 Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
465 return;
466 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700467 authority.enabled = sync;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700468 writeAccountInfoLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 }
Joe Onorato8294fad2009-07-15 16:08:44 -0700470
Fred Quintana77c560f2010-03-29 22:20:26 -0700471 if (sync) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800472 requestSync(account, userId, providerName, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700473 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700474 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 }
476
Amith Yamasani04e0d262012-02-14 11:50:53 -0800477 public int getIsSyncable(Account account, int userId, String providerName) {
Fred Quintana5e787c42009-08-16 23:13:53 -0700478 synchronized (mAuthorities) {
479 if (account != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800480 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
Fred Quintana5e787c42009-08-16 23:13:53 -0700481 "getIsSyncable");
482 if (authority == null) {
483 return -1;
484 }
485 return authority.syncable;
486 }
487
488 int i = mAuthorities.size();
489 while (i > 0) {
490 i--;
Costin Manolache360e4542009-09-04 13:36:04 -0700491 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintana5e787c42009-08-16 23:13:53 -0700492 if (authority.authority.equals(providerName)) {
493 return authority.syncable;
494 }
495 }
496 return -1;
497 }
498 }
499
Amith Yamasani04e0d262012-02-14 11:50:53 -0800500 public void setIsSyncable(Account account, int userId, String providerName, int syncable) {
Fred Quintanab763ab22009-08-18 18:07:30 -0700501 if (syncable > 1) {
502 syncable = 1;
503 } else if (syncable < -1) {
504 syncable = -1;
505 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800506 Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName
507 + ", user " + userId + " -> " + syncable);
Fred Quintana5e787c42009-08-16 23:13:53 -0700508 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800509 AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
510 false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700511 if (authority.syncable == syncable) {
512 Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
513 return;
514 }
Fred Quintana5e787c42009-08-16 23:13:53 -0700515 authority.syncable = syncable;
516 writeAccountInfoLocked();
517 }
518
Fred Quintana77c560f2010-03-29 22:20:26 -0700519 if (syncable > 0) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800520 requestSync(account, userId, providerName, new Bundle());
Fred Quintana5e787c42009-08-16 23:13:53 -0700521 }
522 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
523 }
524
Amith Yamasani04e0d262012-02-14 11:50:53 -0800525 public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800526 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800527 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
528 "getBackoff");
Fred Quintana307da1a2010-01-21 14:24:20 -0800529 if (authority == null || authority.backoffTime < 0) {
530 return null;
531 }
532 return Pair.create(authority.backoffTime, authority.backoffDelay);
533 }
534 }
535
Amith Yamasani04e0d262012-02-14 11:50:53 -0800536 public void setBackoff(Account account, int userId, String providerName,
Fred Quintana307da1a2010-01-21 14:24:20 -0800537 long nextSyncTime, long nextDelay) {
538 if (Log.isLoggable(TAG, Log.VERBOSE)) {
539 Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800540 + ", user " + userId
Fred Quintana307da1a2010-01-21 14:24:20 -0800541 + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
542 }
543 boolean changed = false;
544 synchronized (mAuthorities) {
545 if (account == null || providerName == null) {
546 for (AccountInfo accountInfo : mAccounts.values()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800547 if (account != null && !account.equals(accountInfo.accountAndUser.account)
548 && userId != accountInfo.accountAndUser.userId) {
549 continue;
550 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800551 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
552 if (providerName != null && !providerName.equals(authorityInfo.authority)) {
553 continue;
554 }
555 if (authorityInfo.backoffTime != nextSyncTime
556 || authorityInfo.backoffDelay != nextDelay) {
557 authorityInfo.backoffTime = nextSyncTime;
558 authorityInfo.backoffDelay = nextDelay;
559 changed = true;
560 }
561 }
562 }
563 } else {
564 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800565 getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */,
566 true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800567 if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
568 return;
569 }
570 authority.backoffTime = nextSyncTime;
571 authority.backoffDelay = nextDelay;
572 changed = true;
573 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800574 }
575
576 if (changed) {
577 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
578 }
579 }
580
Alon Alberted1d2532011-02-15 14:02:14 -0800581 public void clearAllBackoffs(SyncQueue syncQueue) {
Alon Albert744e310f2010-12-14 11:37:20 -0800582 boolean changed = false;
583 synchronized (mAuthorities) {
584 for (AccountInfo accountInfo : mAccounts.values()) {
585 for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
586 if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
587 || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
588 if (Log.isLoggable(TAG, Log.VERBOSE)) {
589 Log.v(TAG, "clearAllBackoffs:"
590 + " authority:" + authorityInfo.authority
Amith Yamasani04e0d262012-02-14 11:50:53 -0800591 + " account:" + accountInfo.accountAndUser.account.name
592 + " user:" + accountInfo.accountAndUser.userId
Alon Albert744e310f2010-12-14 11:37:20 -0800593 + " backoffTime was: " + authorityInfo.backoffTime
594 + " backoffDelay was: " + authorityInfo.backoffDelay);
595 }
596 authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
597 authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800598 syncQueue.onBackoffChanged(accountInfo.accountAndUser.account,
599 accountInfo.accountAndUser.userId, authorityInfo.authority, 0);
Alon Albert744e310f2010-12-14 11:37:20 -0800600 changed = true;
601 }
602 }
603 }
604 }
605
606 if (changed) {
607 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
608 }
609 }
610
Amith Yamasani04e0d262012-02-14 11:50:53 -0800611 public void setDelayUntilTime(Account account, int userId, String providerName,
612 long delayUntil) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800613 if (Log.isLoggable(TAG, Log.VERBOSE)) {
614 Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
Amith Yamasani04e0d262012-02-14 11:50:53 -0800615 + ", user " + userId + " -> delayUntil " + delayUntil);
Fred Quintana307da1a2010-01-21 14:24:20 -0800616 }
617 synchronized (mAuthorities) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800618 AuthorityInfo authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -0800619 account, userId, providerName, -1 /* ident */, true);
Fred Quintana307da1a2010-01-21 14:24:20 -0800620 if (authority.delayUntil == delayUntil) {
621 return;
622 }
623 authority.delayUntil = delayUntil;
Fred Quintana307da1a2010-01-21 14:24:20 -0800624 }
625
626 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
627 }
628
Amith Yamasani04e0d262012-02-14 11:50:53 -0800629 public long getDelayUntilTime(Account account, int userId, String providerName) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800630 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800631 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
632 "getDelayUntil");
Fred Quintana307da1a2010-01-21 14:24:20 -0800633 if (authority == null) {
634 return 0;
635 }
636 return authority.delayUntil;
637 }
638 }
639
Amith Yamasani04e0d262012-02-14 11:50:53 -0800640 private void updateOrRemovePeriodicSync(Account account, int userId, String providerName,
641 Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800642 long period, boolean add) {
643 if (period <= 0) {
644 period = 0;
645 }
646 if (extras == null) {
647 extras = new Bundle();
648 }
649 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800650 Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId
651 + ", provider " + providerName
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800652 + " -> period " + period + ", extras " + extras);
653 }
654 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700655 try {
656 AuthorityInfo authority =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800657 getOrCreateAuthorityLocked(account, userId, providerName, -1, false);
Fred Quintana77c560f2010-03-29 22:20:26 -0700658 if (add) {
659 // add this periodic sync if one with the same extras doesn't already
660 // exist in the periodicSyncs array
661 boolean alreadyPresent = false;
662 for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
663 Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i);
664 final Bundle existingExtras = syncInfo.first;
665 if (equals(existingExtras, extras)) {
666 if (syncInfo.second == period) {
667 return;
668 }
669 authority.periodicSyncs.set(i, Pair.create(extras, period));
670 alreadyPresent = true;
671 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800672 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700673 }
674 // if we added an entry to the periodicSyncs array also add an entry to
675 // the periodic syncs status to correspond to it
676 if (!alreadyPresent) {
677 authority.periodicSyncs.add(Pair.create(extras, period));
678 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
679 status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0);
680 }
681 } else {
682 // remove any periodic syncs that match the authority and extras
683 SyncStatusInfo status = mSyncStatus.get(authority.ident);
684 boolean changed = false;
685 Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator();
686 int i = 0;
687 while (iterator.hasNext()) {
688 Pair<Bundle, Long> syncInfo = iterator.next();
689 if (equals(syncInfo.first, extras)) {
690 iterator.remove();
691 changed = true;
692 // if we removed an entry from the periodicSyncs array also
693 // remove the corresponding entry from the status
694 if (status != null) {
695 status.removePeriodicSyncTime(i);
696 }
697 } else {
698 i++;
699 }
700 }
701 if (!changed) {
702 return;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800703 }
704 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700705 } finally {
706 writeAccountInfoLocked();
707 writeStatusLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800708 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800709 }
710
711 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
712 }
713
Amith Yamasani04e0d262012-02-14 11:50:53 -0800714 public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800715 long pollFrequency) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800716 updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency,
717 true /* add */);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800718 }
719
Amith Yamasani04e0d262012-02-14 11:50:53 -0800720 public void removePeriodicSync(Account account, int userId, String providerName,
721 Bundle extras) {
722 updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800723 false /* remove */);
724 }
725
Amith Yamasani04e0d262012-02-14 11:50:53 -0800726 public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800727 ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
728 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800729 AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
730 "getPeriodicSyncs");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800731 if (authority != null) {
732 for (Pair<Bundle, Long> item : authority.periodicSyncs) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800733 syncs.add(new PeriodicSync(account, providerName, item.first,
734 item.second));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800735 }
736 }
737 }
738 return syncs;
739 }
740
Amith Yamasani04e0d262012-02-14 11:50:53 -0800741 public void setMasterSyncAutomatically(boolean flag, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700742 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800743 Boolean auto = mMasterSyncAutomatically.get(userId);
744 if (auto != null && (boolean) auto == flag) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700745 return;
746 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800747 mMasterSyncAutomatically.put(userId, flag);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700748 writeAccountInfoLocked();
749 }
Fred Quintana77c560f2010-03-29 22:20:26 -0700750 if (flag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800751 requestSync(null, userId, null, new Bundle());
Joe Onorato8294fad2009-07-15 16:08:44 -0700752 }
Fred Quintanaac9385e2009-06-22 18:00:59 -0700753 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
754 mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700755 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756
Amith Yamasani04e0d262012-02-14 11:50:53 -0800757 public boolean getMasterSyncAutomatically(int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700758 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800759 Boolean auto = mMasterSyncAutomatically.get(userId);
760 return auto == null ? true : auto;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700761 }
762 }
Costin Manolache360e4542009-09-04 13:36:04 -0700763
Amith Yamasani04e0d262012-02-14 11:50:53 -0800764 public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700765 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800766 return getOrCreateAuthorityLocked(account, userId, authority,
Fred Quintana1bbcd102010-02-10 10:04:33 -0800767 -1 /* assign a new identifier if creating a new authority */,
768 true /* write to storage if this results in a change */);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700769 }
770 }
Costin Manolache360e4542009-09-04 13:36:04 -0700771
Amith Yamasani04e0d262012-02-14 11:50:53 -0800772 public void removeAuthority(Account account, int userId, String authority) {
Fred Quintana7620f1a2010-03-16 15:58:44 -0700773 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800774 removeAuthorityLocked(account, userId, authority, true /* doWrite */);
Fred Quintana7620f1a2010-03-16 15:58:44 -0700775 }
776 }
777
Dianne Hackborn231cc602009-04-27 17:10:36 -0700778 public AuthorityInfo getAuthority(int authorityId) {
779 synchronized (mAuthorities) {
780 return mAuthorities.get(authorityId);
781 }
782 }
Costin Manolache360e4542009-09-04 13:36:04 -0700783
Dianne Hackborn231cc602009-04-27 17:10:36 -0700784 /**
785 * Returns true if there is currently a sync operation for the given
Fred Quintana918339a2010-10-05 14:00:39 -0700786 * account or authority actively being processed.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700787 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800788 public boolean isSyncActive(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700789 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800790 for (SyncInfo syncInfo : getCurrentSyncs(userId)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700791 AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700792 if (ainfo != null && ainfo.account.equals(account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800793 && ainfo.authority.equals(authority)
794 && ainfo.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700795 return true;
796 }
797 }
798 }
Costin Manolache360e4542009-09-04 13:36:04 -0700799
Dianne Hackborn231cc602009-04-27 17:10:36 -0700800 return false;
801 }
Costin Manolache360e4542009-09-04 13:36:04 -0700802
Dianne Hackborn231cc602009-04-27 17:10:36 -0700803 public PendingOperation insertIntoPending(PendingOperation op) {
804 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700805 if (Log.isLoggable(TAG, Log.VERBOSE)) {
806 Log.v(TAG, "insertIntoPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800807 + " user=" + op.userId
808 + " auth=" + op.authority
809 + " src=" + op.syncSource
810 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700811 }
Costin Manolache360e4542009-09-04 13:36:04 -0700812
Amith Yamasani04e0d262012-02-14 11:50:53 -0800813 AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700814 op.authority,
815 -1 /* desired identifier */,
816 true /* write accounts to storage */);
817 if (authority == null) {
818 return null;
819 }
Costin Manolache360e4542009-09-04 13:36:04 -0700820
Dianne Hackborn231cc602009-04-27 17:10:36 -0700821 op = new PendingOperation(op);
822 op.authorityId = authority.ident;
823 mPendingOperations.add(op);
824 appendPendingOperationLocked(op);
Costin Manolache360e4542009-09-04 13:36:04 -0700825
Dianne Hackborn231cc602009-04-27 17:10:36 -0700826 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
827 status.pending = true;
828 }
Costin Manolache360e4542009-09-04 13:36:04 -0700829
Fred Quintanaac9385e2009-06-22 18:00:59 -0700830 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700831 return op;
832 }
833
834 public boolean deleteFromPending(PendingOperation op) {
835 boolean res = false;
836 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700837 if (Log.isLoggable(TAG, Log.VERBOSE)) {
838 Log.v(TAG, "deleteFromPending: account=" + op.account
Amith Yamasani04e0d262012-02-14 11:50:53 -0800839 + " user=" + op.userId
Dianne Hackborn231cc602009-04-27 17:10:36 -0700840 + " auth=" + op.authority
841 + " src=" + op.syncSource
842 + " extras=" + op.extras);
Fred Quintana77c560f2010-03-29 22:20:26 -0700843 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700844 if (mPendingOperations.remove(op)) {
845 if (mPendingOperations.size() == 0
846 || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
847 writePendingOperationsLocked();
848 mNumPendingFinished = 0;
849 } else {
850 mNumPendingFinished++;
851 }
Costin Manolache360e4542009-09-04 13:36:04 -0700852
Amith Yamasani04e0d262012-02-14 11:50:53 -0800853 AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority,
Dianne Hackborn231cc602009-04-27 17:10:36 -0700854 "deleteFromPending");
855 if (authority != null) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700856 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700857 final int N = mPendingOperations.size();
858 boolean morePending = false;
859 for (int i=0; i<N; i++) {
860 PendingOperation cur = mPendingOperations.get(i);
861 if (cur.account.equals(op.account)
Amith Yamasani04e0d262012-02-14 11:50:53 -0800862 && cur.authority.equals(op.authority)
863 && cur.userId == op.userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700864 morePending = true;
865 break;
866 }
867 }
Costin Manolache360e4542009-09-04 13:36:04 -0700868
Dianne Hackborn231cc602009-04-27 17:10:36 -0700869 if (!morePending) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700870 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700871 SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
872 status.pending = false;
873 }
874 }
Costin Manolache360e4542009-09-04 13:36:04 -0700875
Dianne Hackborn231cc602009-04-27 17:10:36 -0700876 res = true;
877 }
878 }
Costin Manolache360e4542009-09-04 13:36:04 -0700879
Fred Quintanaac9385e2009-06-22 18:00:59 -0700880 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700881 return res;
882 }
883
Dianne Hackborn231cc602009-04-27 17:10:36 -0700884 /**
885 * Return a copy of the current array of pending operations. The
886 * PendingOperation objects are the real objects stored inside, so that
887 * they can be used with deleteFromPending().
888 */
889 public ArrayList<PendingOperation> getPendingOperations() {
890 synchronized (mAuthorities) {
891 return new ArrayList<PendingOperation>(mPendingOperations);
892 }
893 }
Costin Manolache360e4542009-09-04 13:36:04 -0700894
Dianne Hackborn231cc602009-04-27 17:10:36 -0700895 /**
896 * Return the number of currently pending operations.
897 */
898 public int getPendingOperationCount() {
899 synchronized (mAuthorities) {
900 return mPendingOperations.size();
901 }
902 }
Costin Manolache360e4542009-09-04 13:36:04 -0700903
Dianne Hackborn231cc602009-04-27 17:10:36 -0700904 /**
905 * Called when the set of account has changed, given the new array of
906 * active accounts.
907 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800908 public void doDatabaseCleanup(Account[] accounts, int userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700909 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -0700910 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts...");
Dianne Hackborn231cc602009-04-27 17:10:36 -0700911 SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
912 Iterator<AccountInfo> accIt = mAccounts.values().iterator();
913 while (accIt.hasNext()) {
914 AccountInfo acc = accIt.next();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800915 if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
916 && acc.accountAndUser.userId == userId) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700917 // This account no longer exists...
Fred Quintana77c560f2010-03-29 22:20:26 -0700918 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800919 Log.w(TAG, "Account removed: " + acc.accountAndUser);
Fred Quintana77c560f2010-03-29 22:20:26 -0700920 }
Dianne Hackborn231cc602009-04-27 17:10:36 -0700921 for (AuthorityInfo auth : acc.authorities.values()) {
922 removing.put(auth.ident, auth);
923 }
924 accIt.remove();
925 }
926 }
Costin Manolache360e4542009-09-04 13:36:04 -0700927
Dianne Hackborn231cc602009-04-27 17:10:36 -0700928 // Clean out all data structures.
929 int i = removing.size();
930 if (i > 0) {
931 while (i > 0) {
932 i--;
933 int ident = removing.keyAt(i);
934 mAuthorities.remove(ident);
935 int j = mSyncStatus.size();
936 while (j > 0) {
937 j--;
938 if (mSyncStatus.keyAt(j) == ident) {
939 mSyncStatus.remove(mSyncStatus.keyAt(j));
940 }
941 }
942 j = mSyncHistory.size();
943 while (j > 0) {
944 j--;
945 if (mSyncHistory.get(j).authorityId == ident) {
946 mSyncHistory.remove(j);
947 }
948 }
949 }
950 writeAccountInfoLocked();
951 writeStatusLocked();
952 writePendingOperationsLocked();
953 writeStatisticsLocked();
954 }
955 }
956 }
957
958 /**
Fred Quintana918339a2010-10-05 14:00:39 -0700959 * Called when a sync is starting. Supply a valid ActiveSyncContext with information
960 * about the sync.
Dianne Hackborn231cc602009-04-27 17:10:36 -0700961 */
Fred Quintana918339a2010-10-05 14:00:39 -0700962 public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
963 final SyncInfo syncInfo;
Dianne Hackborn231cc602009-04-27 17:10:36 -0700964 synchronized (mAuthorities) {
Fred Quintana918339a2010-10-05 14:00:39 -0700965 if (Log.isLoggable(TAG, Log.VERBOSE)) {
966 Log.v(TAG, "setActiveSync: account="
967 + activeSyncContext.mSyncOperation.account
968 + " auth=" + activeSyncContext.mSyncOperation.authority
969 + " src=" + activeSyncContext.mSyncOperation.syncSource
970 + " extras=" + activeSyncContext.mSyncOperation.extras);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700971 }
Fred Quintana918339a2010-10-05 14:00:39 -0700972 AuthorityInfo authority = getOrCreateAuthorityLocked(
973 activeSyncContext.mSyncOperation.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800974 activeSyncContext.mSyncOperation.userId,
Fred Quintana918339a2010-10-05 14:00:39 -0700975 activeSyncContext.mSyncOperation.authority,
976 -1 /* assign a new identifier if creating a new authority */,
977 true /* write to storage if this results in a change */);
978 syncInfo = new SyncInfo(authority.ident,
979 authority.account, authority.authority,
980 activeSyncContext.mStartTime);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800981 getCurrentSyncs(authority.userId).add(syncInfo);
Dianne Hackborn231cc602009-04-27 17:10:36 -0700982 }
Costin Manolache360e4542009-09-04 13:36:04 -0700983
Fred Quintana918339a2010-10-05 14:00:39 -0700984 reportActiveChange();
985 return syncInfo;
986 }
987
988 /**
989 * Called to indicate that a previously active sync is no longer active.
990 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800991 public void removeActiveSync(SyncInfo syncInfo, int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -0700992 synchronized (mAuthorities) {
993 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800994 Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
995 + " user=" + userId
996 + " auth=" + syncInfo.authority);
Fred Quintana918339a2010-10-05 14:00:39 -0700997 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800998 getCurrentSyncs(userId).remove(syncInfo);
Fred Quintana918339a2010-10-05 14:00:39 -0700999 }
1000
1001 reportActiveChange();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001002 }
1003
1004 /**
1005 * To allow others to send active change reports, to poke clients.
1006 */
1007 public void reportActiveChange() {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001008 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001009 }
Costin Manolache360e4542009-09-04 13:36:04 -07001010
Dianne Hackborn231cc602009-04-27 17:10:36 -07001011 /**
1012 * Note that sync has started for the given account and authority.
1013 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001014 public long insertStartSyncEvent(Account accountName, int userId, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001015 long now, int source) {
1016 long id;
1017 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001018 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001019 Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId
Dianne Hackborn231cc602009-04-27 17:10:36 -07001020 + " auth=" + authorityName + " source=" + source);
Fred Quintana77c560f2010-03-29 22:20:26 -07001021 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001022 AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001023 "insertStartSyncEvent");
1024 if (authority == null) {
1025 return -1;
1026 }
1027 SyncHistoryItem item = new SyncHistoryItem();
1028 item.authorityId = authority.ident;
1029 item.historyId = mNextHistoryId++;
1030 if (mNextHistoryId < 0) mNextHistoryId = 0;
1031 item.eventTime = now;
1032 item.source = source;
1033 item.event = EVENT_START;
1034 mSyncHistory.add(0, item);
1035 while (mSyncHistory.size() > MAX_HISTORY) {
1036 mSyncHistory.remove(mSyncHistory.size()-1);
1037 }
1038 id = item.historyId;
Fred Quintana77c560f2010-03-29 22:20:26 -07001039 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001040 }
Costin Manolache360e4542009-09-04 13:36:04 -07001041
Fred Quintanaac9385e2009-06-22 18:00:59 -07001042 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001043 return id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001046 public static boolean equals(Bundle b1, Bundle b2) {
1047 if (b1.size() != b2.size()) {
1048 return false;
1049 }
1050 if (b1.isEmpty()) {
1051 return true;
1052 }
1053 for (String key : b1.keySet()) {
1054 if (!b2.containsKey(key)) {
1055 return false;
1056 }
1057 if (!b1.get(key).equals(b2.get(key))) {
1058 return false;
1059 }
1060 }
1061 return true;
1062 }
1063
Fred Quintana77c560f2010-03-29 22:20:26 -07001064 public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 long downstreamActivity, long upstreamActivity) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001066 synchronized (mAuthorities) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001067 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1068 Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
1069 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001070 SyncHistoryItem item = null;
1071 int i = mSyncHistory.size();
1072 while (i > 0) {
1073 i--;
1074 item = mSyncHistory.get(i);
1075 if (item.historyId == historyId) {
1076 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001078 item = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
Costin Manolache360e4542009-09-04 13:36:04 -07001080
Dianne Hackborn231cc602009-04-27 17:10:36 -07001081 if (item == null) {
1082 Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
1083 return;
1084 }
Costin Manolache360e4542009-09-04 13:36:04 -07001085
Dianne Hackborn231cc602009-04-27 17:10:36 -07001086 item.elapsedTime = elapsedTime;
1087 item.event = EVENT_STOP;
1088 item.mesg = resultMessage;
1089 item.downstreamActivity = downstreamActivity;
1090 item.upstreamActivity = upstreamActivity;
Costin Manolache360e4542009-09-04 13:36:04 -07001091
Dianne Hackborn231cc602009-04-27 17:10:36 -07001092 SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
Costin Manolache360e4542009-09-04 13:36:04 -07001093
Dianne Hackborn231cc602009-04-27 17:10:36 -07001094 status.numSyncs++;
1095 status.totalElapsedTime += elapsedTime;
1096 switch (item.source) {
1097 case SOURCE_LOCAL:
1098 status.numSourceLocal++;
1099 break;
1100 case SOURCE_POLL:
1101 status.numSourcePoll++;
1102 break;
1103 case SOURCE_USER:
1104 status.numSourceUser++;
1105 break;
1106 case SOURCE_SERVER:
1107 status.numSourceServer++;
1108 break;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001109 case SOURCE_PERIODIC:
1110 status.numSourcePeriodic++;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001111 break;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001112 }
Costin Manolache360e4542009-09-04 13:36:04 -07001113
Dianne Hackborn231cc602009-04-27 17:10:36 -07001114 boolean writeStatisticsNow = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -07001115 int day = getCurrentDayLocked();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001116 if (mDayStats[0] == null) {
1117 mDayStats[0] = new DayStats(day);
1118 } else if (day != mDayStats[0].day) {
1119 System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1);
1120 mDayStats[0] = new DayStats(day);
1121 writeStatisticsNow = true;
1122 } else if (mDayStats[0] == null) {
1123 }
1124 final DayStats ds = mDayStats[0];
Costin Manolache360e4542009-09-04 13:36:04 -07001125
Dianne Hackborn231cc602009-04-27 17:10:36 -07001126 final long lastSyncTime = (item.eventTime + elapsedTime);
1127 boolean writeStatusNow = false;
1128 if (MESG_SUCCESS.equals(resultMessage)) {
1129 // - if successful, update the successful columns
1130 if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) {
1131 writeStatusNow = true;
1132 }
1133 status.lastSuccessTime = lastSyncTime;
1134 status.lastSuccessSource = item.source;
1135 status.lastFailureTime = 0;
1136 status.lastFailureSource = -1;
1137 status.lastFailureMesg = null;
1138 status.initialFailureTime = 0;
1139 ds.successCount++;
1140 ds.successTime += elapsedTime;
1141 } else if (!MESG_CANCELED.equals(resultMessage)) {
1142 if (status.lastFailureTime == 0) {
1143 writeStatusNow = true;
1144 }
1145 status.lastFailureTime = lastSyncTime;
1146 status.lastFailureSource = item.source;
1147 status.lastFailureMesg = resultMessage;
1148 if (status.initialFailureTime == 0) {
1149 status.initialFailureTime = lastSyncTime;
1150 }
1151 ds.failureCount++;
1152 ds.failureTime += elapsedTime;
1153 }
Costin Manolache360e4542009-09-04 13:36:04 -07001154
Dianne Hackborn231cc602009-04-27 17:10:36 -07001155 if (writeStatusNow) {
1156 writeStatusLocked();
1157 } else if (!hasMessages(MSG_WRITE_STATUS)) {
1158 sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
1159 WRITE_STATUS_DELAY);
1160 }
1161 if (writeStatisticsNow) {
1162 writeStatisticsLocked();
1163 } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
1164 sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
1165 WRITE_STATISTICS_DELAY);
Costin Manolache360e4542009-09-04 13:36:04 -07001166 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001167 }
Costin Manolache360e4542009-09-04 13:36:04 -07001168
Fred Quintanaac9385e2009-06-22 18:00:59 -07001169 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001170 }
1171
1172 /**
Fred Quintana918339a2010-10-05 14:00:39 -07001173 * Return a list of the currently active syncs. Note that the returned items are the
1174 * real, live active sync objects, so be careful what you do with it.
1175 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001176 public List<SyncInfo> getCurrentSyncs(int userId) {
Fred Quintana918339a2010-10-05 14:00:39 -07001177 synchronized (mAuthorities) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001178 ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId);
1179 if (syncs == null) {
1180 syncs = new ArrayList<SyncInfo>();
1181 mCurrentSyncs.put(userId, syncs);
1182 }
Amith Yamasani1b6ae002012-03-14 14:53:36 -07001183 return syncs;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001184 }
1185 }
Costin Manolache360e4542009-09-04 13:36:04 -07001186
Dianne Hackborn231cc602009-04-27 17:10:36 -07001187 /**
1188 * Return an array of the current sync status for all authorities. Note
1189 * that the objects inside the array are the real, live status objects,
1190 * so be careful what you do with them.
1191 */
1192 public ArrayList<SyncStatusInfo> getSyncStatus() {
1193 synchronized (mAuthorities) {
1194 final int N = mSyncStatus.size();
1195 ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
1196 for (int i=0; i<N; i++) {
1197 ops.add(mSyncStatus.valueAt(i));
1198 }
1199 return ops;
1200 }
1201 }
Costin Manolache360e4542009-09-04 13:36:04 -07001202
Dianne Hackborn231cc602009-04-27 17:10:36 -07001203 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001204 * Return an array of the current authorities. Note
1205 * that the objects inside the array are the real, live objects,
1206 * so be careful what you do with them.
1207 */
1208 public ArrayList<AuthorityInfo> getAuthorities() {
1209 synchronized (mAuthorities) {
1210 final int N = mAuthorities.size();
1211 ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N);
1212 for (int i=0; i<N; i++) {
1213 infos.add(mAuthorities.valueAt(i));
1214 }
1215 return infos;
1216 }
1217 }
1218
1219 /**
Costin Manolacheb7520982009-09-02 18:03:05 -07001220 * Returns the status that matches the authority and account.
1221 *
1222 * @param account the account we want to check
Dianne Hackborn231cc602009-04-27 17:10:36 -07001223 * @param authority the authority whose row should be selected
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001224 * @return the SyncStatusInfo for the authority
Dianne Hackborn231cc602009-04-27 17:10:36 -07001225 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001226 public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId,
1227 String authority) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001228 if (account == null || authority == null) {
1229 throw new IllegalArgumentException();
1230 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001231 synchronized (mAuthorities) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001232 final int N = mSyncStatus.size();
1233 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001234 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001235 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
Costin Manolacheb7520982009-09-02 18:03:05 -07001236
Amith Yamasani04e0d262012-02-14 11:50:53 -08001237 if (ainfo != null && ainfo.authority.equals(authority)
1238 && ainfo.userId == userId
1239 && account.equals(ainfo.account)) {
Costin Manolacheb7520982009-09-02 18:03:05 -07001240 return cur;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001241 }
1242 }
Costin Manolacheb7520982009-09-02 18:03:05 -07001243 return null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001244 }
1245 }
Costin Manolache360e4542009-09-04 13:36:04 -07001246
Dianne Hackborn231cc602009-04-27 17:10:36 -07001247 /**
1248 * Return true if the pending status is true of any matching authorities.
1249 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001250 public boolean isSyncPending(Account account, int userId, String authority) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001251 synchronized (mAuthorities) {
1252 final int N = mSyncStatus.size();
1253 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001254 SyncStatusInfo cur = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001255 AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
1256 if (ainfo == null) {
1257 continue;
1258 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001259 if (userId != ainfo.userId) {
1260 continue;
1261 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001262 if (account != null && !ainfo.account.equals(account)) {
1263 continue;
1264 }
1265 if (ainfo.authority.equals(authority) && cur.pending) {
1266 return true;
1267 }
1268 }
1269 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 }
1271 }
1272
1273 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001274 * Return an array of the current sync status for all authorities. Note
1275 * that the objects inside the array are the real, live status objects,
1276 * so be careful what you do with them.
1277 */
1278 public ArrayList<SyncHistoryItem> getSyncHistory() {
1279 synchronized (mAuthorities) {
1280 final int N = mSyncHistory.size();
1281 ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N);
1282 for (int i=0; i<N; i++) {
1283 items.add(mSyncHistory.get(i));
1284 }
1285 return items;
1286 }
1287 }
Costin Manolache360e4542009-09-04 13:36:04 -07001288
Dianne Hackborn231cc602009-04-27 17:10:36 -07001289 /**
1290 * Return an array of the current per-day statistics. Note
1291 * that the objects inside the array are the real, live status objects,
1292 * so be careful what you do with them.
1293 */
1294 public DayStats[] getDayStatistics() {
1295 synchronized (mAuthorities) {
1296 DayStats[] ds = new DayStats[mDayStats.length];
1297 System.arraycopy(mDayStats, 0, ds, 0, ds.length);
1298 return ds;
1299 }
1300 }
Costin Manolache360e4542009-09-04 13:36:04 -07001301
Dianne Hackborn55280a92009-05-07 15:53:46 -07001302 private int getCurrentDayLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001303 mCal.setTimeInMillis(System.currentTimeMillis());
1304 final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
1305 if (mYear != mCal.get(Calendar.YEAR)) {
1306 mYear = mCal.get(Calendar.YEAR);
1307 mCal.clear();
1308 mCal.set(Calendar.YEAR, mYear);
1309 mYearInDays = (int)(mCal.getTimeInMillis()/86400000);
1310 }
1311 return dayOfYear + mYearInDays;
1312 }
Costin Manolache360e4542009-09-04 13:36:04 -07001313
Dianne Hackborn231cc602009-04-27 17:10:36 -07001314 /**
1315 * Retrieve an authority, returning null if one does not exist.
Costin Manolache360e4542009-09-04 13:36:04 -07001316 *
Dianne Hackborn231cc602009-04-27 17:10:36 -07001317 * @param accountName The name of the account for the authority.
1318 * @param authorityName The name of the authority itself.
1319 * @param tag If non-null, this will be used in a log message if the
1320 * requested authority does not exist.
1321 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08001322 private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001323 String tag) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001324 AccountAndUser au = new AccountAndUser(accountName, userId);
1325 AccountInfo accountInfo = mAccounts.get(au);
1326 if (accountInfo == null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001327 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001328 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001329 Log.v(TAG, tag + ": unknown account " + au);
Fred Quintanab763ab22009-08-18 18:07:30 -07001330 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001331 }
1332 return null;
1333 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001334 AuthorityInfo authority = accountInfo.authorities.get(authorityName);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001335 if (authority == null) {
1336 if (tag != null) {
Fred Quintanab763ab22009-08-18 18:07:30 -07001337 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1338 Log.v(TAG, tag + ": unknown authority " + authorityName);
1339 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001340 }
1341 return null;
1342 }
Costin Manolache360e4542009-09-04 13:36:04 -07001343
Dianne Hackborn231cc602009-04-27 17:10:36 -07001344 return authority;
1345 }
Costin Manolache360e4542009-09-04 13:36:04 -07001346
Amith Yamasani04e0d262012-02-14 11:50:53 -08001347 private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId,
Dianne Hackborn231cc602009-04-27 17:10:36 -07001348 String authorityName, int ident, boolean doWrite) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001349 AccountAndUser au = new AccountAndUser(accountName, userId);
1350 AccountInfo account = mAccounts.get(au);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001351 if (account == null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001352 account = new AccountInfo(au);
1353 mAccounts.put(au, account);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001354 }
1355 AuthorityInfo authority = account.authorities.get(authorityName);
1356 if (authority == null) {
1357 if (ident < 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001358 ident = mNextAuthorityId;
1359 mNextAuthorityId++;
1360 doWrite = true;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001361 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001362 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1363 Log.v(TAG, "created a new AuthorityInfo for " + accountName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001364 + ", user " + userId
1365 + ", provider " + authorityName);
Fred Quintana77c560f2010-03-29 22:20:26 -07001366 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001367 authority = new AuthorityInfo(accountName, userId, authorityName, ident);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001368 account.authorities.put(authorityName, authority);
1369 mAuthorities.put(ident, authority);
1370 if (doWrite) {
1371 writeAccountInfoLocked();
1372 }
1373 }
Costin Manolache360e4542009-09-04 13:36:04 -07001374
Dianne Hackborn231cc602009-04-27 17:10:36 -07001375 return authority;
1376 }
Costin Manolache360e4542009-09-04 13:36:04 -07001377
Amith Yamasani04e0d262012-02-14 11:50:53 -08001378 private void removeAuthorityLocked(Account account, int userId, String authorityName,
1379 boolean doWrite) {
1380 AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
Fred Quintana7620f1a2010-03-16 15:58:44 -07001381 if (accountInfo != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001382 final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
1383 if (authorityInfo != null) {
1384 mAuthorities.remove(authorityInfo.ident);
Fred Quintana77c560f2010-03-29 22:20:26 -07001385 if (doWrite) {
1386 writeAccountInfoLocked();
1387 }
Fred Quintana7620f1a2010-03-16 15:58:44 -07001388 }
1389 }
1390 }
1391
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001392 public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) {
1393 synchronized (mAuthorities) {
1394 return getOrCreateSyncStatusLocked(authority.ident);
1395 }
1396 }
1397
Dianne Hackborn231cc602009-04-27 17:10:36 -07001398 private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
1399 SyncStatusInfo status = mSyncStatus.get(authorityId);
1400 if (status == null) {
1401 status = new SyncStatusInfo(authorityId);
1402 mSyncStatus.put(authorityId, status);
1403 }
1404 return status;
1405 }
Costin Manolache360e4542009-09-04 13:36:04 -07001406
Dianne Hackborn55280a92009-05-07 15:53:46 -07001407 public void writeAllState() {
1408 synchronized (mAuthorities) {
1409 // Account info is always written so no need to do it here.
Costin Manolache360e4542009-09-04 13:36:04 -07001410
Dianne Hackborn55280a92009-05-07 15:53:46 -07001411 if (mNumPendingFinished > 0) {
1412 // Only write these if they are out of date.
1413 writePendingOperationsLocked();
1414 }
Costin Manolache360e4542009-09-04 13:36:04 -07001415
Dianne Hackborn55280a92009-05-07 15:53:46 -07001416 // Just always write these... they are likely out of date.
1417 writeStatusLocked();
1418 writeStatisticsLocked();
1419 }
1420 }
Costin Manolache360e4542009-09-04 13:36:04 -07001421
Dianne Hackborn231cc602009-04-27 17:10:36 -07001422 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001423 * public for testing
1424 */
1425 public void clearAndReadState() {
1426 synchronized (mAuthorities) {
1427 mAuthorities.clear();
1428 mAccounts.clear();
1429 mPendingOperations.clear();
1430 mSyncStatus.clear();
1431 mSyncHistory.clear();
1432
1433 readAccountInfoLocked();
1434 readStatusLocked();
1435 readPendingOperationsLocked();
1436 readStatisticsLocked();
Fred Quintana77c560f2010-03-29 22:20:26 -07001437 readAndDeleteLegacyAccountInfoLocked();
1438 writeAccountInfoLocked();
1439 writeStatusLocked();
1440 writePendingOperationsLocked();
1441 writeStatisticsLocked();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001442 }
1443 }
1444
1445 /**
Dianne Hackborn231cc602009-04-27 17:10:36 -07001446 * Read all account information back in to the initial engine state.
1447 */
1448 private void readAccountInfoLocked() {
Fred Quintana77c560f2010-03-29 22:20:26 -07001449 int highestAuthorityId = -1;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001450 FileInputStream fis = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001452 fis = mAccountInfoFile.openRead();
1453 if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
1454 XmlPullParser parser = Xml.newPullParser();
1455 parser.setInput(fis, null);
1456 int eventType = parser.getEventType();
1457 while (eventType != XmlPullParser.START_TAG) {
1458 eventType = parser.next();
1459 }
1460 String tagName = parser.getName();
1461 if ("accounts".equals(tagName)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001462 String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
Fred Quintanac2e46912010-03-15 16:10:44 -07001463 String versionString = parser.getAttributeValue(null, "version");
1464 int version;
1465 try {
1466 version = (versionString == null) ? 0 : Integer.parseInt(versionString);
1467 } catch (NumberFormatException e) {
1468 version = 0;
1469 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001470 String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
Fred Quintana77c560f2010-03-29 22:20:26 -07001471 try {
1472 int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
1473 mNextAuthorityId = Math.max(mNextAuthorityId, id);
1474 } catch (NumberFormatException e) {
1475 // don't care
Fred Quintanac2e46912010-03-15 16:10:44 -07001476 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07001477 String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET);
1478 try {
1479 mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString);
1480 } catch (NumberFormatException e) {
1481 mSyncRandomOffset = 0;
1482 }
1483 if (mSyncRandomOffset == 0) {
1484 Random random = new Random(System.currentTimeMillis());
1485 mSyncRandomOffset = random.nextInt(86400);
1486 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001487 mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
Dianne Hackborn231cc602009-04-27 17:10:36 -07001488 eventType = parser.next();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001489 AuthorityInfo authority = null;
1490 Pair<Bundle, Long> periodicSync = null;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001491 do {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001492 if (eventType == XmlPullParser.START_TAG) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001493 tagName = parser.getName();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001494 if (parser.getDepth() == 2) {
1495 if ("authority".equals(tagName)) {
Fred Quintanac2e46912010-03-15 16:10:44 -07001496 authority = parseAuthority(parser, version);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001497 periodicSync = null;
Fred Quintana77c560f2010-03-29 22:20:26 -07001498 if (authority.ident > highestAuthorityId) {
1499 highestAuthorityId = authority.ident;
1500 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001501 } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) {
1502 parseListenForTickles(parser);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001503 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001504 } else if (parser.getDepth() == 3) {
1505 if ("periodicSync".equals(tagName) && authority != null) {
1506 periodicSync = parsePeriodicSync(parser, authority);
1507 }
1508 } else if (parser.getDepth() == 4 && periodicSync != null) {
1509 if ("extra".equals(tagName)) {
1510 parseExtra(parser, periodicSync);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001511 }
1512 }
1513 }
1514 eventType = parser.next();
1515 } while (eventType != XmlPullParser.END_DOCUMENT);
1516 }
1517 } catch (XmlPullParserException e) {
1518 Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001519 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001520 } catch (java.io.IOException e) {
1521 if (fis == null) Log.i(TAG, "No initial accounts");
1522 else Log.w(TAG, "Error reading accounts", e);
Fred Quintanac2e46912010-03-15 16:10:44 -07001523 return;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001524 } finally {
Fred Quintana77c560f2010-03-29 22:20:26 -07001525 mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001526 if (fis != null) {
1527 try {
1528 fis.close();
1529 } catch (java.io.IOException e1) {
1530 }
1531 }
1532 }
Fred Quintanac2e46912010-03-15 16:10:44 -07001533
Fred Quintana77c560f2010-03-29 22:20:26 -07001534 maybeMigrateSettingsForRenamedAuthorities();
Dianne Hackborn231cc602009-04-27 17:10:36 -07001535 }
Costin Manolache360e4542009-09-04 13:36:04 -07001536
Fred Quintanafb084402010-03-23 17:57:03 -07001537 /**
1538 * some authority names have changed. copy over their settings and delete the old ones
1539 * @return true if a change was made
1540 */
1541 private boolean maybeMigrateSettingsForRenamedAuthorities() {
1542 boolean writeNeeded = false;
1543
1544 ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
1545 final int N = mAuthorities.size();
1546 for (int i=0; i<N; i++) {
1547 AuthorityInfo authority = mAuthorities.valueAt(i);
1548 // skip this authority if it isn't one of the renamed ones
1549 final String newAuthorityName = sAuthorityRenames.get(authority.authority);
1550 if (newAuthorityName == null) {
1551 continue;
1552 }
1553
1554 // remember this authority so we can remove it later. we can't remove it
1555 // now without messing up this loop iteration
1556 authoritiesToRemove.add(authority);
1557
1558 // this authority isn't enabled, no need to copy it to the new authority name since
1559 // the default is "disabled"
1560 if (!authority.enabled) {
1561 continue;
1562 }
1563
1564 // if we already have a record of this new authority then don't copy over the settings
Amith Yamasani04e0d262012-02-14 11:50:53 -08001565 if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup")
1566 != null) {
Fred Quintanafb084402010-03-23 17:57:03 -07001567 continue;
1568 }
1569
1570 AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001571 authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001572 newAuthority.enabled = true;
1573 writeNeeded = true;
1574 }
1575
1576 for (AuthorityInfo authorityInfo : authoritiesToRemove) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001577 removeAuthorityLocked(authorityInfo.account, authorityInfo.userId,
1578 authorityInfo.authority, false /* doWrite */);
Fred Quintanafb084402010-03-23 17:57:03 -07001579 writeNeeded = true;
1580 }
1581
1582 return writeNeeded;
1583 }
1584
Amith Yamasani04e0d262012-02-14 11:50:53 -08001585 private void parseListenForTickles(XmlPullParser parser) {
1586 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1587 int userId = 0;
1588 try {
1589 userId = Integer.parseInt(user);
1590 } catch (NumberFormatException e) {
1591 Log.e(TAG, "error parsing the user for listen-for-tickles", e);
1592 } catch (NullPointerException e) {
1593 Log.e(TAG, "the user in listen-for-tickles is null", e);
1594 }
1595 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
1596 boolean listen = enabled == null || Boolean.parseBoolean(enabled);
1597 mMasterSyncAutomatically.put(userId, listen);
1598 }
1599
Fred Quintanac2e46912010-03-15 16:10:44 -07001600 private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001601 AuthorityInfo authority = null;
1602 int id = -1;
1603 try {
1604 id = Integer.parseInt(parser.getAttributeValue(
1605 null, "id"));
1606 } catch (NumberFormatException e) {
1607 Log.e(TAG, "error parsing the id of the authority", e);
1608 } catch (NullPointerException e) {
1609 Log.e(TAG, "the id of the authority is null", e);
1610 }
1611 if (id >= 0) {
Fred Quintanafb084402010-03-23 17:57:03 -07001612 String authorityName = parser.getAttributeValue(null, "authority");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001613 String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
Fred Quintanafb084402010-03-23 17:57:03 -07001614 String syncable = parser.getAttributeValue(null, "syncable");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001615 String accountName = parser.getAttributeValue(null, "account");
1616 String accountType = parser.getAttributeValue(null, "type");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001617 String user = parser.getAttributeValue(null, XML_ATTR_USER);
1618 int userId = user == null ? 0 : Integer.parseInt(user);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001619 if (accountType == null) {
1620 accountType = "com.google";
Fred Quintanafb084402010-03-23 17:57:03 -07001621 syncable = "unknown";
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001622 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001623 authority = mAuthorities.get(id);
1624 if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
1625 + accountName + " auth=" + authorityName
Amith Yamasani04e0d262012-02-14 11:50:53 -08001626 + " user=" + userId
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001627 + " enabled=" + enabled
1628 + " syncable=" + syncable);
1629 if (authority == null) {
1630 if (DEBUG_FILE) Log.v(TAG, "Creating entry");
1631 authority = getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001632 new Account(accountName, accountType), userId, authorityName, id, false);
Fred Quintanac2e46912010-03-15 16:10:44 -07001633 // If the version is 0 then we are upgrading from a file format that did not
1634 // know about periodic syncs. In that case don't clear the list since we
1635 // want the default, which is a daily periodioc sync.
1636 // Otherwise clear out this default list since we will populate it later with
1637 // the periodic sync descriptions that are read from the configuration file.
1638 if (version > 0) {
1639 authority.periodicSyncs.clear();
1640 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001641 }
1642 if (authority != null) {
1643 authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
1644 if ("unknown".equals(syncable)) {
1645 authority.syncable = -1;
1646 } else {
1647 authority.syncable =
Fred Quintanafb084402010-03-23 17:57:03 -07001648 (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001649 }
1650 } else {
1651 Log.w(TAG, "Failure adding authority: account="
1652 + accountName + " auth=" + authorityName
1653 + " enabled=" + enabled
1654 + " syncable=" + syncable);
1655 }
1656 }
1657
1658 return authority;
1659 }
1660
1661 private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
1662 Bundle extras = new Bundle();
1663 String periodValue = parser.getAttributeValue(null, "period");
1664 final long period;
1665 try {
1666 period = Long.parseLong(periodValue);
1667 } catch (NumberFormatException e) {
1668 Log.e(TAG, "error parsing the period of a periodic sync", e);
1669 return null;
1670 } catch (NullPointerException e) {
1671 Log.e(TAG, "the period of a periodic sync is null", e);
1672 return null;
1673 }
1674 final Pair<Bundle, Long> periodicSync = Pair.create(extras, period);
1675 authority.periodicSyncs.add(periodicSync);
Fred Quintanac2e46912010-03-15 16:10:44 -07001676
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001677 return periodicSync;
1678 }
1679
1680 private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) {
1681 final Bundle extras = periodicSync.first;
1682 String name = parser.getAttributeValue(null, "name");
1683 String type = parser.getAttributeValue(null, "type");
1684 String value1 = parser.getAttributeValue(null, "value1");
1685 String value2 = parser.getAttributeValue(null, "value2");
1686
1687 try {
1688 if ("long".equals(type)) {
1689 extras.putLong(name, Long.parseLong(value1));
1690 } else if ("integer".equals(type)) {
1691 extras.putInt(name, Integer.parseInt(value1));
1692 } else if ("double".equals(type)) {
1693 extras.putDouble(name, Double.parseDouble(value1));
1694 } else if ("float".equals(type)) {
1695 extras.putFloat(name, Float.parseFloat(value1));
1696 } else if ("boolean".equals(type)) {
1697 extras.putBoolean(name, Boolean.parseBoolean(value1));
1698 } else if ("string".equals(type)) {
1699 extras.putString(name, value1);
1700 } else if ("account".equals(type)) {
1701 extras.putParcelable(name, new Account(value1, value2));
1702 }
1703 } catch (NumberFormatException e) {
1704 Log.e(TAG, "error parsing bundle value", e);
1705 } catch (NullPointerException e) {
1706 Log.e(TAG, "error parsing bundle value", e);
1707 }
1708 }
1709
Dianne Hackborn231cc602009-04-27 17:10:36 -07001710 /**
1711 * Write all account information to the account file.
1712 */
1713 private void writeAccountInfoLocked() {
1714 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
1715 FileOutputStream fos = null;
Costin Manolache360e4542009-09-04 13:36:04 -07001716
Dianne Hackborn231cc602009-04-27 17:10:36 -07001717 try {
1718 fos = mAccountInfoFile.startWrite();
1719 XmlSerializer out = new FastXmlSerializer();
1720 out.setOutput(fos, "utf-8");
1721 out.startDocument(null, true);
1722 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
Costin Manolache360e4542009-09-04 13:36:04 -07001723
Dianne Hackborn231cc602009-04-27 17:10:36 -07001724 out.startTag(null, "accounts");
Fred Quintanac2e46912010-03-15 16:10:44 -07001725 out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001726 out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
Ashish Sharma69d95de2012-04-11 17:27:24 -07001727 out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001728
1729 // Write the Sync Automatically flags for each user
1730 final int M = mMasterSyncAutomatically.size();
1731 for (int m = 0; m < M; m++) {
1732 int userId = mMasterSyncAutomatically.keyAt(m);
1733 Boolean listen = mMasterSyncAutomatically.valueAt(m);
1734 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
1735 out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
1736 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
1737 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001738 }
Costin Manolache360e4542009-09-04 13:36:04 -07001739
Dianne Hackborn231cc602009-04-27 17:10:36 -07001740 final int N = mAuthorities.size();
1741 for (int i=0; i<N; i++) {
Costin Manolache360e4542009-09-04 13:36:04 -07001742 AuthorityInfo authority = mAuthorities.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001743 out.startTag(null, "authority");
1744 out.attribute(null, "id", Integer.toString(authority.ident));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001745 out.attribute(null, "account", authority.account.name);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001746 out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId));
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001747 out.attribute(null, "type", authority.account.type);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001748 out.attribute(null, "authority", authority.authority);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001749 out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
Fred Quintana5e787c42009-08-16 23:13:53 -07001750 if (authority.syncable < 0) {
1751 out.attribute(null, "syncable", "unknown");
Fred Quintanafb084402010-03-23 17:57:03 -07001752 } else {
1753 out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0));
Fred Quintana5e787c42009-08-16 23:13:53 -07001754 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001755 for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) {
1756 out.startTag(null, "periodicSync");
1757 out.attribute(null, "period", Long.toString(periodicSync.second));
1758 final Bundle extras = periodicSync.first;
1759 for (String key : extras.keySet()) {
1760 out.startTag(null, "extra");
1761 out.attribute(null, "name", key);
1762 final Object value = extras.get(key);
1763 if (value instanceof Long) {
1764 out.attribute(null, "type", "long");
1765 out.attribute(null, "value1", value.toString());
1766 } else if (value instanceof Integer) {
1767 out.attribute(null, "type", "integer");
1768 out.attribute(null, "value1", value.toString());
1769 } else if (value instanceof Boolean) {
1770 out.attribute(null, "type", "boolean");
1771 out.attribute(null, "value1", value.toString());
1772 } else if (value instanceof Float) {
1773 out.attribute(null, "type", "float");
1774 out.attribute(null, "value1", value.toString());
1775 } else if (value instanceof Double) {
1776 out.attribute(null, "type", "double");
1777 out.attribute(null, "value1", value.toString());
1778 } else if (value instanceof String) {
1779 out.attribute(null, "type", "string");
1780 out.attribute(null, "value1", value.toString());
1781 } else if (value instanceof Account) {
1782 out.attribute(null, "type", "account");
1783 out.attribute(null, "value1", ((Account)value).name);
1784 out.attribute(null, "value2", ((Account)value).type);
1785 }
1786 out.endTag(null, "extra");
1787 }
1788 out.endTag(null, "periodicSync");
1789 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001790 out.endTag(null, "authority");
1791 }
Costin Manolache360e4542009-09-04 13:36:04 -07001792
Dianne Hackborn231cc602009-04-27 17:10:36 -07001793 out.endTag(null, "accounts");
Costin Manolache360e4542009-09-04 13:36:04 -07001794
Dianne Hackborn231cc602009-04-27 17:10:36 -07001795 out.endDocument();
Costin Manolache360e4542009-09-04 13:36:04 -07001796
Dianne Hackborn231cc602009-04-27 17:10:36 -07001797 mAccountInfoFile.finishWrite(fos);
1798 } catch (java.io.IOException e1) {
1799 Log.w(TAG, "Error writing accounts", e1);
1800 if (fos != null) {
1801 mAccountInfoFile.failWrite(fos);
1802 }
1803 }
1804 }
Costin Manolache360e4542009-09-04 13:36:04 -07001805
Dianne Hackborn231cc602009-04-27 17:10:36 -07001806 static int getIntColumn(Cursor c, String name) {
1807 return c.getInt(c.getColumnIndex(name));
1808 }
Costin Manolache360e4542009-09-04 13:36:04 -07001809
Dianne Hackborn231cc602009-04-27 17:10:36 -07001810 static long getLongColumn(Cursor c, String name) {
1811 return c.getLong(c.getColumnIndex(name));
1812 }
Costin Manolache360e4542009-09-04 13:36:04 -07001813
Dianne Hackborn231cc602009-04-27 17:10:36 -07001814 /**
1815 * Load sync engine state from the old syncmanager database, and then
1816 * erase it. Note that we don't deal with pending operations, active
1817 * sync, or history.
1818 */
Fred Quintana77c560f2010-03-29 22:20:26 -07001819 private void readAndDeleteLegacyAccountInfoLocked() {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001820 // Look for old database to initialize from.
1821 File file = mContext.getDatabasePath("syncmanager.db");
1822 if (!file.exists()) {
1823 return;
1824 }
1825 String path = file.getPath();
1826 SQLiteDatabase db = null;
1827 try {
1828 db = SQLiteDatabase.openDatabase(path, null,
1829 SQLiteDatabase.OPEN_READONLY);
1830 } catch (SQLiteException e) {
1831 }
Costin Manolache360e4542009-09-04 13:36:04 -07001832
Dianne Hackborn231cc602009-04-27 17:10:36 -07001833 if (db != null) {
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001834 final boolean hasType = db.getVersion() >= 11;
Costin Manolache360e4542009-09-04 13:36:04 -07001835
Dianne Hackborn231cc602009-04-27 17:10:36 -07001836 // Copy in all of the status information, as well as accounts.
1837 if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
1838 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1839 qb.setTables("stats, status");
1840 HashMap<String,String> map = new HashMap<String,String>();
1841 map.put("_id", "status._id as _id");
1842 map.put("account", "stats.account as account");
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001843 if (hasType) {
1844 map.put("account_type", "stats.account_type as account_type");
1845 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001846 map.put("authority", "stats.authority as authority");
1847 map.put("totalElapsedTime", "totalElapsedTime");
1848 map.put("numSyncs", "numSyncs");
1849 map.put("numSourceLocal", "numSourceLocal");
1850 map.put("numSourcePoll", "numSourcePoll");
1851 map.put("numSourceServer", "numSourceServer");
1852 map.put("numSourceUser", "numSourceUser");
1853 map.put("lastSuccessSource", "lastSuccessSource");
1854 map.put("lastSuccessTime", "lastSuccessTime");
1855 map.put("lastFailureSource", "lastFailureSource");
1856 map.put("lastFailureTime", "lastFailureTime");
1857 map.put("lastFailureMesg", "lastFailureMesg");
1858 map.put("pending", "pending");
1859 qb.setProjectionMap(map);
1860 qb.appendWhere("stats._id = status.stats_id");
1861 Cursor c = qb.query(db, null, null, null, null, null, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 while (c.moveToNext()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001863 String accountName = c.getString(c.getColumnIndex("account"));
Dianne Hackborn2d5ed1f2009-05-06 15:22:38 -07001864 String accountType = hasType
1865 ? c.getString(c.getColumnIndex("account_type")) : null;
Dianne Hackborn7a135592009-05-06 00:28:37 -07001866 if (accountType == null) {
Costin Manolache3348f142009-09-29 18:58:36 -07001867 accountType = "com.google";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001869 String authorityName = c.getString(c.getColumnIndex("authority"));
1870 AuthorityInfo authority = this.getOrCreateAuthorityLocked(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001871 new Account(accountName, accountType), 0 /* legacy is single-user */,
Dianne Hackborn7a135592009-05-06 00:28:37 -07001872 authorityName, -1, false);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001873 if (authority != null) {
1874 int i = mSyncStatus.size();
1875 boolean found = false;
1876 SyncStatusInfo st = null;
1877 while (i > 0) {
1878 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001879 st = mSyncStatus.valueAt(i);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001880 if (st.authorityId == authority.ident) {
1881 found = true;
1882 break;
1883 }
1884 }
1885 if (!found) {
1886 st = new SyncStatusInfo(authority.ident);
1887 mSyncStatus.put(authority.ident, st);
1888 }
1889 st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
1890 st.numSyncs = getIntColumn(c, "numSyncs");
1891 st.numSourceLocal = getIntColumn(c, "numSourceLocal");
1892 st.numSourcePoll = getIntColumn(c, "numSourcePoll");
1893 st.numSourceServer = getIntColumn(c, "numSourceServer");
1894 st.numSourceUser = getIntColumn(c, "numSourceUser");
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001895 st.numSourcePeriodic = 0;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001896 st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
1897 st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
1898 st.lastFailureSource = getIntColumn(c, "lastFailureSource");
1899 st.lastFailureTime = getLongColumn(c, "lastFailureTime");
1900 st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
1901 st.pending = getIntColumn(c, "pending") != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 }
Costin Manolache360e4542009-09-04 13:36:04 -07001904
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001906
Dianne Hackborn231cc602009-04-27 17:10:36 -07001907 // Retrieve the settings.
1908 qb = new SQLiteQueryBuilder();
1909 qb.setTables("settings");
1910 c = qb.query(db, null, null, null, null, null, null);
1911 while (c.moveToNext()) {
1912 String name = c.getString(c.getColumnIndex("name"));
1913 String value = c.getString(c.getColumnIndex("value"));
1914 if (name == null) continue;
1915 if (name.equals("listen_for_tickles")) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001916 setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001917 } else if (name.startsWith("sync_provider_")) {
1918 String provider = name.substring("sync_provider_".length(),
1919 name.length());
Fred Quintanaac9385e2009-06-22 18:00:59 -07001920 int i = mAuthorities.size();
1921 while (i > 0) {
1922 i--;
Costin Manolache360e4542009-09-04 13:36:04 -07001923 AuthorityInfo authority = mAuthorities.valueAt(i);
Fred Quintanaac9385e2009-06-22 18:00:59 -07001924 if (authority.authority.equals(provider)) {
1925 authority.enabled = value == null || Boolean.parseBoolean(value);
Fred Quintana5e787c42009-08-16 23:13:53 -07001926 authority.syncable = 1;
Fred Quintanaac9385e2009-06-22 18:00:59 -07001927 }
1928 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 }
Costin Manolache360e4542009-09-04 13:36:04 -07001931
Dianne Hackborn231cc602009-04-27 17:10:36 -07001932 c.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001933
Dianne Hackborn231cc602009-04-27 17:10:36 -07001934 db.close();
Costin Manolache360e4542009-09-04 13:36:04 -07001935
Dianne Hackborn231cc602009-04-27 17:10:36 -07001936 (new File(path)).delete();
1937 }
1938 }
Costin Manolache360e4542009-09-04 13:36:04 -07001939
Dianne Hackborn231cc602009-04-27 17:10:36 -07001940 public static final int STATUS_FILE_END = 0;
1941 public static final int STATUS_FILE_ITEM = 100;
Costin Manolache360e4542009-09-04 13:36:04 -07001942
Dianne Hackborn231cc602009-04-27 17:10:36 -07001943 /**
1944 * Read all sync status back in to the initial engine state.
1945 */
1946 private void readStatusLocked() {
1947 if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
1948 try {
1949 byte[] data = mStatusFile.readFully();
1950 Parcel in = Parcel.obtain();
1951 in.unmarshall(data, 0, data.length);
1952 in.setDataPosition(0);
1953 int token;
1954 while ((token=in.readInt()) != STATUS_FILE_END) {
1955 if (token == STATUS_FILE_ITEM) {
1956 SyncStatusInfo status = new SyncStatusInfo(in);
1957 if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
1958 status.pending = false;
1959 if (DEBUG_FILE) Log.v(TAG, "Adding status for id "
1960 + status.authorityId);
1961 mSyncStatus.put(status.authorityId, status);
1962 }
1963 } else {
1964 // Ooops.
1965 Log.w(TAG, "Unknown status token: " + token);
1966 break;
1967 }
1968 }
1969 } catch (java.io.IOException e) {
1970 Log.i(TAG, "No initial status");
1971 }
1972 }
Costin Manolache360e4542009-09-04 13:36:04 -07001973
Dianne Hackborn231cc602009-04-27 17:10:36 -07001974 /**
1975 * Write all sync status to the sync status file.
1976 */
1977 private void writeStatusLocked() {
1978 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07001979
Dianne Hackborn231cc602009-04-27 17:10:36 -07001980 // The file is being written, so we don't need to have a scheduled
1981 // write until the next change.
1982 removeMessages(MSG_WRITE_STATUS);
Costin Manolache360e4542009-09-04 13:36:04 -07001983
Dianne Hackborn231cc602009-04-27 17:10:36 -07001984 FileOutputStream fos = null;
1985 try {
1986 fos = mStatusFile.startWrite();
1987 Parcel out = Parcel.obtain();
1988 final int N = mSyncStatus.size();
1989 for (int i=0; i<N; i++) {
1990 SyncStatusInfo status = mSyncStatus.valueAt(i);
1991 out.writeInt(STATUS_FILE_ITEM);
1992 status.writeToParcel(out, 0);
1993 }
1994 out.writeInt(STATUS_FILE_END);
1995 fos.write(out.marshall());
1996 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07001997
Dianne Hackborn231cc602009-04-27 17:10:36 -07001998 mStatusFile.finishWrite(fos);
1999 } catch (java.io.IOException e1) {
2000 Log.w(TAG, "Error writing status", e1);
2001 if (fos != null) {
2002 mStatusFile.failWrite(fos);
2003 }
2004 }
2005 }
Costin Manolache360e4542009-09-04 13:36:04 -07002006
Fred Quintana307da1a2010-01-21 14:24:20 -08002007 public static final int PENDING_OPERATION_VERSION = 2;
Costin Manolache360e4542009-09-04 13:36:04 -07002008
Dianne Hackborn231cc602009-04-27 17:10:36 -07002009 /**
2010 * Read all pending operations back in to the initial engine state.
2011 */
2012 private void readPendingOperationsLocked() {
2013 if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile());
2014 try {
2015 byte[] data = mPendingFile.readFully();
2016 Parcel in = Parcel.obtain();
2017 in.unmarshall(data, 0, data.length);
2018 in.setDataPosition(0);
2019 final int SIZE = in.dataSize();
2020 while (in.dataPosition() < SIZE) {
2021 int version = in.readInt();
Fred Quintana307da1a2010-01-21 14:24:20 -08002022 if (version != PENDING_OPERATION_VERSION && version != 1) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002023 Log.w(TAG, "Unknown pending operation version "
2024 + version + "; dropping all ops");
2025 break;
2026 }
2027 int authorityId = in.readInt();
2028 int syncSource = in.readInt();
2029 byte[] flatExtras = in.createByteArray();
Fred Quintana307da1a2010-01-21 14:24:20 -08002030 boolean expedited;
2031 if (version == PENDING_OPERATION_VERSION) {
2032 expedited = in.readInt() != 0;
2033 } else {
2034 expedited = false;
2035 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002036 AuthorityInfo authority = mAuthorities.get(authorityId);
2037 if (authority != null) {
Fred Quintana5695c7b2010-12-06 15:07:52 -08002038 Bundle extras;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002039 if (flatExtras != null) {
2040 extras = unflattenBundle(flatExtras);
Fred Quintana5695c7b2010-12-06 15:07:52 -08002041 } else {
2042 // if we are unable to parse the extras for whatever reason convert this
2043 // to a regular sync by creating an empty extras
2044 extras = new Bundle();
Dianne Hackborn231cc602009-04-27 17:10:36 -07002045 }
2046 PendingOperation op = new PendingOperation(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002047 authority.account, authority.userId, syncSource,
Fred Quintana307da1a2010-01-21 14:24:20 -08002048 authority.authority, extras, expedited);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002049 op.authorityId = authorityId;
2050 op.flatExtras = flatExtras;
2051 if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
2052 + " auth=" + op.authority
2053 + " src=" + op.syncSource
Fred Quintana307da1a2010-01-21 14:24:20 -08002054 + " expedited=" + op.expedited
Dianne Hackborn231cc602009-04-27 17:10:36 -07002055 + " extras=" + op.extras);
2056 mPendingOperations.add(op);
2057 }
2058 }
2059 } catch (java.io.IOException e) {
2060 Log.i(TAG, "No initial pending operations");
2061 }
2062 }
Costin Manolache360e4542009-09-04 13:36:04 -07002063
Dianne Hackborn231cc602009-04-27 17:10:36 -07002064 private void writePendingOperationLocked(PendingOperation op, Parcel out) {
2065 out.writeInt(PENDING_OPERATION_VERSION);
2066 out.writeInt(op.authorityId);
2067 out.writeInt(op.syncSource);
2068 if (op.flatExtras == null && op.extras != null) {
2069 op.flatExtras = flattenBundle(op.extras);
2070 }
2071 out.writeByteArray(op.flatExtras);
Fred Quintana307da1a2010-01-21 14:24:20 -08002072 out.writeInt(op.expedited ? 1 : 0);
Dianne Hackborn231cc602009-04-27 17:10:36 -07002073 }
Costin Manolache360e4542009-09-04 13:36:04 -07002074
Dianne Hackborn231cc602009-04-27 17:10:36 -07002075 /**
2076 * Write all currently pending ops to the pending ops file.
2077 */
2078 private void writePendingOperationsLocked() {
2079 final int N = mPendingOperations.size();
2080 FileOutputStream fos = null;
2081 try {
2082 if (N == 0) {
2083 if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
2084 mPendingFile.truncate();
2085 return;
2086 }
Costin Manolache360e4542009-09-04 13:36:04 -07002087
Dianne Hackborn231cc602009-04-27 17:10:36 -07002088 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
2089 fos = mPendingFile.startWrite();
Costin Manolache360e4542009-09-04 13:36:04 -07002090
Dianne Hackborn231cc602009-04-27 17:10:36 -07002091 Parcel out = Parcel.obtain();
2092 for (int i=0; i<N; i++) {
2093 PendingOperation op = mPendingOperations.get(i);
2094 writePendingOperationLocked(op, out);
2095 }
2096 fos.write(out.marshall());
2097 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002098
Dianne Hackborn231cc602009-04-27 17:10:36 -07002099 mPendingFile.finishWrite(fos);
2100 } catch (java.io.IOException e1) {
2101 Log.w(TAG, "Error writing pending operations", e1);
2102 if (fos != null) {
2103 mPendingFile.failWrite(fos);
2104 }
2105 }
2106 }
Costin Manolache360e4542009-09-04 13:36:04 -07002107
Dianne Hackborn231cc602009-04-27 17:10:36 -07002108 /**
2109 * Append the given operation to the pending ops file; if unable to,
2110 * write all pending ops.
2111 */
2112 private void appendPendingOperationLocked(PendingOperation op) {
2113 if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
2114 FileOutputStream fos = null;
2115 try {
2116 fos = mPendingFile.openAppend();
2117 } catch (java.io.IOException e) {
2118 if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file");
2119 writePendingOperationsLocked();
2120 return;
2121 }
Costin Manolache360e4542009-09-04 13:36:04 -07002122
Dianne Hackborn231cc602009-04-27 17:10:36 -07002123 try {
2124 Parcel out = Parcel.obtain();
2125 writePendingOperationLocked(op, out);
2126 fos.write(out.marshall());
2127 out.recycle();
2128 } catch (java.io.IOException e1) {
2129 Log.w(TAG, "Error writing pending operations", e1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 } finally {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002131 try {
2132 fos.close();
2133 } catch (java.io.IOException e2) {
2134 }
2135 }
2136 }
Costin Manolache360e4542009-09-04 13:36:04 -07002137
Dianne Hackborn231cc602009-04-27 17:10:36 -07002138 static private byte[] flattenBundle(Bundle bundle) {
2139 byte[] flatData = null;
2140 Parcel parcel = Parcel.obtain();
2141 try {
2142 bundle.writeToParcel(parcel, 0);
2143 flatData = parcel.marshall();
2144 } finally {
2145 parcel.recycle();
2146 }
2147 return flatData;
2148 }
Costin Manolache360e4542009-09-04 13:36:04 -07002149
Dianne Hackborn231cc602009-04-27 17:10:36 -07002150 static private Bundle unflattenBundle(byte[] flatData) {
2151 Bundle bundle;
2152 Parcel parcel = Parcel.obtain();
2153 try {
2154 parcel.unmarshall(flatData, 0, flatData.length);
2155 parcel.setDataPosition(0);
2156 bundle = parcel.readBundle();
2157 } catch (RuntimeException e) {
2158 // A RuntimeException is thrown if we were unable to parse the parcel.
2159 // Create an empty parcel in this case.
2160 bundle = new Bundle();
2161 } finally {
2162 parcel.recycle();
2163 }
2164 return bundle;
2165 }
Costin Manolache360e4542009-09-04 13:36:04 -07002166
Amith Yamasani04e0d262012-02-14 11:50:53 -08002167 private void requestSync(Account account, int userId, String authority, Bundle extras) {
2168 // If this is happening in the system process, then call the syncrequest listener
2169 // to make a request back to the SyncManager directly.
2170 // If this is probably a test instance, then call back through the ContentResolver
2171 // which will know which userId to apply based on the Binder id.
2172 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
2173 && mSyncRequestListener != null) {
2174 mSyncRequestListener.onSyncRequest(account, userId, authority, extras);
2175 } else {
2176 ContentResolver.requestSync(account, authority, extras);
2177 }
2178 }
2179
Dianne Hackborn231cc602009-04-27 17:10:36 -07002180 public static final int STATISTICS_FILE_END = 0;
2181 public static final int STATISTICS_FILE_ITEM_OLD = 100;
2182 public static final int STATISTICS_FILE_ITEM = 101;
Costin Manolache360e4542009-09-04 13:36:04 -07002183
Dianne Hackborn231cc602009-04-27 17:10:36 -07002184 /**
2185 * Read all sync statistics back in to the initial engine state.
2186 */
2187 private void readStatisticsLocked() {
2188 try {
2189 byte[] data = mStatisticsFile.readFully();
2190 Parcel in = Parcel.obtain();
2191 in.unmarshall(data, 0, data.length);
2192 in.setDataPosition(0);
2193 int token;
2194 int index = 0;
2195 while ((token=in.readInt()) != STATISTICS_FILE_END) {
2196 if (token == STATISTICS_FILE_ITEM
2197 || token == STATISTICS_FILE_ITEM_OLD) {
2198 int day = in.readInt();
2199 if (token == STATISTICS_FILE_ITEM_OLD) {
2200 day = day - 2009 + 14245; // Magic!
2201 }
2202 DayStats ds = new DayStats(day);
2203 ds.successCount = in.readInt();
2204 ds.successTime = in.readLong();
2205 ds.failureCount = in.readInt();
2206 ds.failureTime = in.readLong();
2207 if (index < mDayStats.length) {
2208 mDayStats[index] = ds;
2209 index++;
2210 }
2211 } else {
2212 // Ooops.
2213 Log.w(TAG, "Unknown stats token: " + token);
2214 break;
2215 }
2216 }
2217 } catch (java.io.IOException e) {
2218 Log.i(TAG, "No initial statistics");
2219 }
2220 }
Costin Manolache360e4542009-09-04 13:36:04 -07002221
Dianne Hackborn231cc602009-04-27 17:10:36 -07002222 /**
2223 * Write all sync statistics to the sync status file.
2224 */
2225 private void writeStatisticsLocked() {
2226 if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
Costin Manolache360e4542009-09-04 13:36:04 -07002227
Dianne Hackborn231cc602009-04-27 17:10:36 -07002228 // The file is being written, so we don't need to have a scheduled
2229 // write until the next change.
2230 removeMessages(MSG_WRITE_STATISTICS);
Costin Manolache360e4542009-09-04 13:36:04 -07002231
Dianne Hackborn231cc602009-04-27 17:10:36 -07002232 FileOutputStream fos = null;
2233 try {
2234 fos = mStatisticsFile.startWrite();
2235 Parcel out = Parcel.obtain();
2236 final int N = mDayStats.length;
2237 for (int i=0; i<N; i++) {
2238 DayStats ds = mDayStats[i];
2239 if (ds == null) {
2240 break;
2241 }
2242 out.writeInt(STATISTICS_FILE_ITEM);
2243 out.writeInt(ds.day);
2244 out.writeInt(ds.successCount);
2245 out.writeLong(ds.successTime);
2246 out.writeInt(ds.failureCount);
2247 out.writeLong(ds.failureTime);
2248 }
2249 out.writeInt(STATISTICS_FILE_END);
2250 fos.write(out.marshall());
2251 out.recycle();
Costin Manolache360e4542009-09-04 13:36:04 -07002252
Dianne Hackborn231cc602009-04-27 17:10:36 -07002253 mStatisticsFile.finishWrite(fos);
2254 } catch (java.io.IOException e1) {
2255 Log.w(TAG, "Error writing stats", e1);
2256 if (fos != null) {
2257 mStatisticsFile.failWrite(fos);
2258 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002259 }
2260 }
2261}