blob: ff1281e5c546ed456b41f334bca209be87c3dcae [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.content;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Alon Alberte0bde332011-09-22 14:26:16 -070019import android.accounts.Account;
Amith Yamasanif29f2362012-04-05 18:29:52 -070020import android.accounts.AccountAndUser;
Alon Alberte0bde332011-09-22 14:26:16 -070021import android.accounts.AccountManager;
Fred Quintana33e44692011-12-05 15:04:16 -080022import android.app.ActivityManager;
Alon Alberte0bde332011-09-22 14:26:16 -070023import android.app.AlarmManager;
Amith Yamasani9422bdc2013-04-10 16:58:19 -070024import android.app.AppGlobals;
Alon Alberte0bde332011-09-22 14:26:16 -070025import android.app.Notification;
26import android.app.NotificationManager;
27import android.app.PendingIntent;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.content.BroadcastReceiver;
29import android.content.ComponentName;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.ISyncAdapter;
33import android.content.ISyncContext;
34import android.content.ISyncStatusObserver;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.ServiceConnection;
38import android.content.SyncActivityTooManyDeletes;
39import android.content.SyncAdapterType;
40import android.content.SyncAdaptersCache;
41import android.content.SyncInfo;
42import android.content.SyncResult;
43import android.content.SyncStatusInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070044import android.content.pm.ApplicationInfo;
Amith Yamasani9422bdc2013-04-10 16:58:19 -070045import android.content.pm.PackageInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070046import android.content.pm.PackageManager;
47import android.content.pm.ProviderInfo;
48import android.content.pm.RegisteredServicesCache;
49import android.content.pm.RegisteredServicesCacheListener;
50import android.content.pm.ResolveInfo;
Amith Yamasani04e0d262012-02-14 11:50:53 -080051import android.content.pm.UserInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070052import android.net.ConnectivityManager;
53import android.net.NetworkInfo;
Fred Quintana918339a2010-10-05 14:00:39 -070054import android.os.Bundle;
55import android.os.Handler;
56import android.os.HandlerThread;
57import android.os.IBinder;
58import android.os.Looper;
59import android.os.Message;
60import android.os.PowerManager;
61import android.os.Process;
62import android.os.RemoteException;
63import android.os.SystemClock;
64import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070065import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070066import android.os.UserManager;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070067import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.text.format.DateUtils;
70import android.text.format.Time;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.util.EventLog;
72import android.util.Log;
Fred Quintana307da1a2010-01-21 14:24:20 -080073import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
Alon Albert8e285552012-09-17 15:05:27 -070075import com.android.internal.R;
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080076import com.android.internal.annotations.GuardedBy;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070077import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080078import com.android.server.accounts.AccountManagerService;
79import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
Alon Albert8e285552012-09-17 15:05:27 -070080import com.google.android.collect.Lists;
81import com.google.android.collect.Maps;
82import com.google.android.collect.Sets;
83
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.io.FileDescriptor;
Alon Albert57286f92012-10-09 14:21:38 -070085import java.io.PrintStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import java.io.PrintWriter;
87import java.util.ArrayList;
Alon Alberte0bde332011-09-22 14:26:16 -070088import java.util.Arrays;
Fred Quintana918339a2010-10-05 14:00:39 -070089import java.util.Collection;
90import java.util.Collections;
Alon Alberte0bde332011-09-22 14:26:16 -070091import java.util.Comparator;
Fred Quintanab3029c32010-04-06 13:27:12 -070092import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070093import java.util.HashSet;
Fred Quintana918339a2010-10-05 14:00:39 -070094import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095import java.util.List;
Alon Alberte0bde332011-09-22 14:26:16 -070096import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097import java.util.Random;
Alon Albert8e285552012-09-17 15:05:27 -070098import java.util.Set;
Fred Quintanae91ebe22009-09-29 20:44:30 -070099import java.util.concurrent.CountDownLatch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
101/**
102 * @hide
103 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700104public class SyncManager {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private static final String TAG = "SyncManager";
106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 /** Delay a sync due to local changes this long. In milliseconds */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700108 private static final long LOCAL_SYNC_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
110 /**
111 * If a sync takes longer than this and the sync queue is not empty then we will
112 * cancel it and add it back to the end of the sync queue. In milliseconds.
113 */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700114 private static final long MAX_TIME_PER_SYNC;
115
116 static {
Fred Quintana33e44692011-12-05 15:04:16 -0800117 final boolean isLargeRAM = ActivityManager.isLargeRAM();
118 int defaultMaxInitSyncs = isLargeRAM ? 5 : 2;
119 int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1;
120 MAX_SIMULTANEOUS_INITIALIZATION_SYNCS =
121 SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs);
122 MAX_SIMULTANEOUS_REGULAR_SYNCS =
123 SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs);
Fred Quintana918339a2010-10-05 14:00:39 -0700124 LOCAL_SYNC_DELAY =
125 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
126 MAX_TIME_PER_SYNC =
127 SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
128 SYNC_NOTIFICATION_DELAY =
129 SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
Fred Quintana918339a2010-10-05 14:00:39 -0700132 private static final long SYNC_NOTIFICATION_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
134 /**
135 * When retrying a sync for the first time use this delay. After that
136 * the retry time will double until it reached MAX_SYNC_RETRY_TIME.
137 * In milliseconds.
138 */
139 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds
140
141 /**
142 * Default the max sync retry time to this value.
143 */
144 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour
145
146 /**
Fred Quintana8570f742010-02-18 10:32:54 -0800147 * How long to wait before retrying a sync that failed due to one already being in progress.
148 */
149 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
150
Fred Quintana3ec47302010-03-10 10:08:31 -0800151 private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
152
Dianne Hackborne746f032010-09-13 16:02:57 -0700153 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700154 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
Fred Quintana918339a2010-10-05 14:00:39 -0700155 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
156
157 private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
158 private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159
160 private Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Amith Yamasani04e0d262012-02-14 11:50:53 -0800162 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
163
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700164 // TODO: add better locking around mRunningAccounts
165 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
Fred Quintana918339a2010-10-05 14:00:39 -0700168 volatile private PowerManager.WakeLock mSyncManagerWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 volatile private boolean mDataConnectionIsConnected = false;
170 volatile private boolean mStorageIsLow = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171
172 private final NotificationManager mNotificationMgr;
173 private AlarmManager mAlarmService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700175 private SyncStorageEngine mSyncStorageEngine;
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700176
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800177 @GuardedBy("mSyncQueue")
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700178 private final SyncQueue mSyncQueue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700180 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 // set if the sync active indicator should be reported
183 private boolean mNeedSyncActiveNotification = false;
184
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 private final PendingIntent mSyncAlarmIntent;
Fred Quintanaf892fb32009-08-27 21:32:08 -0700186 // Synchronized on "this". Instead of using this directly one should instead call
187 // its accessor, getConnManager().
188 private ConnectivityManager mConnManagerDoNotUseDirectly;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700190 protected SyncAdaptersCache mSyncAdapters;
Fred Quintana718d8a22009-04-29 17:53:20 -0700191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 private BroadcastReceiver mStorageIntentReceiver =
193 new BroadcastReceiver() {
194 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 String action = intent.getAction();
196 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
197 if (Log.isLoggable(TAG, Log.VERBOSE)) {
198 Log.v(TAG, "Internal storage is low.");
199 }
200 mStorageIsLow = true;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700201 cancelActiveSync(null /* any account */, UserHandle.USER_ALL,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800202 null /* any authority */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
204 if (Log.isLoggable(TAG, Log.VERBOSE)) {
205 Log.v(TAG, "Internal storage is ok.");
206 }
207 mStorageIsLow = false;
208 sendCheckAlarmsMessage();
209 }
210 }
211 };
212
Fred Quintana60307342009-03-24 22:48:12 -0700213 private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
214 public void onReceive(Context context, Intent intent) {
Fred Quintanae91ebe22009-09-29 20:44:30 -0700215 mSyncHandler.onBootCompleted();
Fred Quintana60307342009-03-24 22:48:12 -0700216 }
217 };
218
Fred Quintana600dde02009-09-23 12:44:10 -0700219 private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
220 public void onReceive(Context context, Intent intent) {
221 if (getConnectivityManager().getBackgroundDataSetting()) {
Alon Albert57286f92012-10-09 14:21:38 -0700222 scheduleSync(null /* account */, UserHandle.USER_ALL,
223 SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED,
224 null /* authority */,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800225 new Bundle(), 0 /* delay */,
Fred Quintana600dde02009-09-23 12:44:10 -0700226 false /* onlyThoseWithUnknownSyncableState */);
227 }
228 }
229 };
230
Amith Yamasanid648a602012-09-26 15:06:10 -0700231 private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
232 public void onReceive(Context context, Intent intent) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700233 updateRunningAccounts();
234
235 // Kick off sync for everyone, since this was a radical account change
Alon Albert57286f92012-10-09 14:21:38 -0700236 scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null,
237 null, 0 /* no delay */, false);
Amith Yamasanid648a602012-09-26 15:06:10 -0700238 }
239 };
240
Fred Quintanab3029c32010-04-06 13:27:12 -0700241 private final PowerManager mPowerManager;
242
Ashish Sharma69d95de2012-04-11 17:27:24 -0700243 // Use this as a random offset to seed all periodic syncs
244 private int mSyncRandomOffsetMillis;
245
Amith Yamasani9535c912012-10-10 21:48:33 -0700246 private final UserManager mUserManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700247
Fred Quintana918339a2010-10-05 14:00:39 -0700248 private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
249 private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
250
Amith Yamasani258848d2012-08-10 17:06:33 -0700251 private List<UserInfo> getAllUsers() {
Amith Yamasani9535c912012-10-10 21:48:33 -0700252 return mUserManager.getUsers();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800253 }
254
255 private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
256 boolean found = false;
257 for (int i = 0; i < accounts.length; i++) {
258 if (accounts[i].userId == userId
259 && accounts[i].account.equals(account)) {
260 found = true;
261 break;
262 }
263 }
264 return found;
265 }
266
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700267 public void updateRunningAccounts() {
268 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800269
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700270 if (mBootCompleted) {
271 doDatabaseCleanup();
272 }
273
Fred Quintana918339a2010-10-05 14:00:39 -0700274 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700275 if (!containsAccountAndUser(mRunningAccounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800276 currentSyncContext.mSyncOperation.account,
277 currentSyncContext.mSyncOperation.userId)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700278 Log.d(TAG, "canceling sync since the account is no longer running");
Fred Quintana918339a2010-10-05 14:00:39 -0700279 sendSyncFinishedOrCanceledMessage(currentSyncContext,
Fred Quintanad9d2f112009-04-23 13:36:27 -0700280 null /* no result since this is a cancel */);
281 }
282 }
283
284 // we must do this since we don't bother scheduling alarms when
285 // the accounts are not set yet
286 sendCheckAlarmsMessage();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700287 }
288
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700289 private void doDatabaseCleanup() {
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700290 for (UserInfo user : mUserManager.getUsers(true)) {
291 // Skip any partially created/removed users
292 if (user.partial) continue;
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700293 Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
294 mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
295 }
296 }
297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 private BroadcastReceiver mConnectivityIntentReceiver =
299 new BroadcastReceiver() {
300 public void onReceive(Context context, Intent intent) {
Alon Alberted1d2532011-02-15 14:02:14 -0800301 final boolean wasConnected = mDataConnectionIsConnected;
302
303 // don't use the intent to figure out if network is connected, just check
304 // ConnectivityManager directly.
Alon Albert1bad83a2011-02-16 10:29:56 -0800305 mDataConnectionIsConnected = readDataConnectionState();
Alon Alberted1d2532011-02-15 14:02:14 -0800306 if (mDataConnectionIsConnected) {
307 if (!wasConnected) {
308 if (Log.isLoggable(TAG, Log.VERBOSE)) {
309 Log.v(TAG, "Reconnection detected: clearing all backoffs");
310 }
311 mSyncStorageEngine.clearAllBackoffs(mSyncQueue);
312 }
313 sendCheckAlarmsMessage();
314 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
316 };
317
Alon Albert1bad83a2011-02-16 10:29:56 -0800318 private boolean readDataConnectionState() {
Alon Alberted1d2532011-02-15 14:02:14 -0800319 NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
320 return (networkInfo != null) && networkInfo.isConnected();
321 }
322
Dianne Hackborn55280a92009-05-07 15:53:46 -0700323 private BroadcastReceiver mShutdownIntentReceiver =
324 new BroadcastReceiver() {
325 public void onReceive(Context context, Intent intent) {
326 Log.w(TAG, "Writing sync state before shutdown...");
327 getSyncStorageEngine().writeAllState();
328 }
329 };
330
Amith Yamasani13593602012-03-22 16:16:17 -0700331 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
332 @Override
333 public void onReceive(Context context, Intent intent) {
Alon Albert8e285552012-09-17 15:05:27 -0700334 String action = intent.getAction();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700335 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
336 if (userId == UserHandle.USER_NULL) return;
337
Alon Albert8e285552012-09-17 15:05:27 -0700338 if (Intent.ACTION_USER_REMOVED.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700339 onUserRemoved(userId);
340 } else if (Intent.ACTION_USER_STARTING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700341 onUserStarting(userId);
342 } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700343 onUserStopping(userId);
Alon Albert8e285552012-09-17 15:05:27 -0700344 }
Amith Yamasani13593602012-03-22 16:16:17 -0700345 }
346 };
347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 private final SyncHandler mSyncHandler;
350
Fred Quintana4f9cfc52009-09-02 15:20:23 -0700351 private volatile boolean mBootCompleted = false;
352
Fred Quintanaf892fb32009-08-27 21:32:08 -0700353 private ConnectivityManager getConnectivityManager() {
354 synchronized (this) {
355 if (mConnManagerDoNotUseDirectly == null) {
356 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
357 Context.CONNECTIVITY_SERVICE);
358 }
359 return mConnManagerDoNotUseDirectly;
360 }
361 }
362
Jeff Sharkeye4996bb2012-10-17 14:16:28 -0700363 /**
364 * Should only be created after {@link ContentService#systemReady()} so that
365 * {@link PackageManager} is ready to query.
366 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 public SyncManager(Context context, boolean factoryTest) {
368 // Initialize the SyncStorageEngine first, before registering observers
369 // and creating threads and so on; it may fail if the disk is full.
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700370 mContext = context;
Amith Yamasani9535c912012-10-10 21:48:33 -0700371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 SyncStorageEngine.init(context);
373 mSyncStorageEngine = SyncStorageEngine.getSingleton();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800374 mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
Alon Albert57286f92012-10-09 14:21:38 -0700375 public void onSyncRequest(Account account, int userId, int reason, String authority,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800376 Bundle extras) {
Alon Albert57286f92012-10-09 14:21:38 -0700377 scheduleSync(account, userId, reason, authority, extras, 0, false);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800378 }
379 });
380
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700381 mSyncAdapters = new SyncAdaptersCache(mContext);
Alon Albert57286f92012-10-09 14:21:38 -0700382 mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383
Fred Quintana307da1a2010-01-21 14:24:20 -0800384 HandlerThread syncThread = new HandlerThread("SyncHandlerThread",
385 Process.THREAD_PRIORITY_BACKGROUND);
386 syncThread.start();
387 mSyncHandler = new SyncHandler(syncThread.getLooper());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388
Fred Quintana44037e62010-01-21 13:14:49 -0800389 mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700390 @Override
391 public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
Fred Quintana44037e62010-01-21 13:14:49 -0800392 if (!removed) {
Alon Albert57286f92012-10-09 14:21:38 -0700393 scheduleSync(null, UserHandle.USER_ALL,
394 SyncOperation.REASON_SERVICE_CHANGED,
395 type.authority, null, 0 /* no delay */,
Fred Quintana44037e62010-01-21 13:14:49 -0800396 false /* onlyThoseWithUnkownSyncableState */);
397 }
398 }
399 }, mSyncHandler);
Fred Quintana718d8a22009-04-29 17:53:20 -0700400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 mSyncAlarmIntent = PendingIntent.getBroadcast(
402 mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
405 context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
406
Fred Quintanae91ebe22009-09-29 20:44:30 -0700407 if (!factoryTest) {
408 intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
409 context.registerReceiver(mBootCompletedReceiver, intentFilter);
410 }
Fred Quintana60307342009-03-24 22:48:12 -0700411
Fred Quintana600dde02009-09-23 12:44:10 -0700412 intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
413 context.registerReceiver(mBackgroundDataSettingChanged, intentFilter);
414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
416 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
417 context.registerReceiver(mStorageIntentReceiver, intentFilter);
418
Dianne Hackborn55280a92009-05-07 15:53:46 -0700419 intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
420 intentFilter.setPriority(100);
421 context.registerReceiver(mShutdownIntentReceiver, intentFilter);
422
Amith Yamasani13593602012-03-22 16:16:17 -0700423 intentFilter = new IntentFilter();
424 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700425 intentFilter.addAction(Intent.ACTION_USER_STARTING);
426 intentFilter.addAction(Intent.ACTION_USER_STOPPING);
Alon Albert8e285552012-09-17 15:05:27 -0700427 mContext.registerReceiverAsUser(
428 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
Amith Yamasani13593602012-03-22 16:16:17 -0700429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 if (!factoryTest) {
431 mNotificationMgr = (NotificationManager)
432 context.getSystemService(Context.NOTIFICATION_SERVICE);
433 context.registerReceiver(new SyncAlarmIntentReceiver(),
434 new IntentFilter(ACTION_SYNC_ALARM));
435 } else {
436 mNotificationMgr = null;
437 }
Fred Quintanab3029c32010-04-06 13:27:12 -0700438 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
Amith Yamasani9535c912012-10-10 21:48:33 -0700439 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
441 // This WakeLock is used to ensure that we stay awake between the time that we receive
442 // a sync alarm notification and when we finish processing it. We need to do this
443 // because we don't do the work in the alarm handler, rather we do it in a message
444 // handler.
Fred Quintanab3029c32010-04-06 13:27:12 -0700445 mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 HANDLE_SYNC_ALARM_WAKE_LOCK);
447 mHandleAlarmWakeLock.setReferenceCounted(false);
448
Fred Quintana918339a2010-10-05 14:00:39 -0700449 // This WakeLock is used to ensure that we stay awake while running the sync loop
450 // message handler. Normally we will hold a sync adapter wake lock while it is being
451 // synced but during the execution of the sync loop it might finish a sync for
452 // one sync adapter before starting the sync for the other sync adapter and we
453 // don't want the device to go to sleep during that window.
454 mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
455 SYNC_LOOP_WAKE_LOCK);
456 mSyncManagerWakeLock.setReferenceCounted(false);
457
Dianne Hackborn231cc602009-04-27 17:10:36 -0700458 mSyncStorageEngine.addStatusChangeListener(
Fred Quintanaac9385e2009-06-22 18:00:59 -0700459 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700460 public void onStatusChanged(int which) {
461 // force the sync loop to run if the settings change
462 sendCheckAlarmsMessage();
463 }
464 });
Fred Quintanae91ebe22009-09-29 20:44:30 -0700465
466 if (!factoryTest) {
Amith Yamasanid648a602012-09-26 15:06:10 -0700467 // Register for account list updates for all users
468 mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
469 UserHandle.ALL,
470 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
471 null, null);
Fred Quintanae91ebe22009-09-29 20:44:30 -0700472 }
Ashish Sharma69d95de2012-04-11 17:27:24 -0700473
474 // Pick a random second in a day to seed all periodic syncs
475 mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 /**
479 * Return a random value v that satisfies minValue <= v < maxValue. The difference between
480 * maxValue and minValue must be less than Integer.MAX_VALUE.
481 */
482 private long jitterize(long minValue, long maxValue) {
483 Random random = new Random(SystemClock.elapsedRealtime());
484 long spread = maxValue - minValue;
485 if (spread > Integer.MAX_VALUE) {
486 throw new IllegalArgumentException("the difference between the maxValue and the "
487 + "minValue must be less than " + Integer.MAX_VALUE);
488 }
489 return minValue + random.nextInt((int)spread);
490 }
491
Dianne Hackborn231cc602009-04-27 17:10:36 -0700492 public SyncStorageEngine getSyncStorageEngine() {
493 return mSyncStorageEngine;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 }
Doug Zongker44f57472009-09-20 15:52:43 -0700495
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700496 public int getIsSyncable(Account account, int userId, String providerName) {
497 int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
498 UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
499
500 // If it's not a restricted user, return isSyncable
501 if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
502
503 // Else check if the sync adapter has opted-in or not
504 RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
505 mSyncAdapters.getServiceInfo(
506 SyncAdapterType.newKey(providerName, account.type), userId);
507 if (syncAdapterInfo == null) return isSyncable;
508
509 PackageInfo pInfo = null;
510 try {
511 pInfo = AppGlobals.getPackageManager().getPackageInfo(
512 syncAdapterInfo.componentName.getPackageName(), 0, userId);
513 if (pInfo == null) return isSyncable;
514 } catch (RemoteException re) {
515 // Shouldn't happen
516 return isSyncable;
517 }
518 if (pInfo.restrictedAccountType != null
519 && pInfo.restrictedAccountType.equals(account.type)) {
520 return isSyncable;
521 } else {
522 return 0;
523 }
524 }
525
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 private void ensureAlarmService() {
527 if (mAlarmService == null) {
528 mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
529 }
530 }
531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 * Initiate a sync. This can start a sync for all providers
534 * (pass null to url, set onlyTicklable to false), only those
535 * providers that are marked as ticklable (pass null to url,
536 * set onlyTicklable to true), or a specific provider (set url
537 * to the content url of the provider).
538 *
539 * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
540 * true then initiate a sync that just checks for local changes to send
541 * to the server, otherwise initiate a sync that first gets any
542 * changes from the server before sending local changes back to
543 * the server.
544 *
545 * <p>If a specific provider is being synced (the url is non-null)
546 * then the extras can contain SyncAdapter-specific information
547 * to control what gets synced (e.g. which specific feed to sync).
548 *
549 * <p>You'll start getting callbacks after this.
550 *
Fred Quintanaac9385e2009-06-22 18:00:59 -0700551 * @param requestedAccount the account to sync, may be null to signify all accounts
Amith Yamasani04e0d262012-02-14 11:50:53 -0800552 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
553 * then all users' accounts are considered.
Alon Albert57286f92012-10-09 14:21:38 -0700554 * @param reason for sync request. If this is a positive integer, it is the Linux uid
555 * assigned to the process that requested the sync. If it's negative, the sync was requested by
556 * the SyncManager itself and could be one of the following:
557 * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
558 * {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
559 * {@link SyncOperation#REASON_SERVICE_CHANGED}
560 * {@link SyncOperation#REASON_PERIODIC}
561 * {@link SyncOperation#REASON_IS_SYNCABLE}
562 * {@link SyncOperation#REASON_SYNC_AUTO}
563 * {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
564 * {@link SyncOperation#REASON_USER_START}
Fred Quintanaac9385e2009-06-22 18:00:59 -0700565 * @param requestedAuthority the authority to sync, may be null to indicate all authorities
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 * @param extras a Map of SyncAdapter-specific information to control
Fred Quintana918339a2010-10-05 14:00:39 -0700567 * syncs of a specific provider. Can be null. Is ignored
568 * if the url is null.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 * @param delay how many milliseconds in the future to wait before performing this
Fred Quintana4a6679b2009-08-17 13:05:39 -0700570 * @param onlyThoseWithUnkownSyncableState
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 */
Alon Albert57286f92012-10-09 14:21:38 -0700572 public void scheduleSync(Account requestedAccount, int userId, int reason,
573 String requestedAuthority, Bundle extras, long delay,
574 boolean onlyThoseWithUnkownSyncableState) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576
Fred Quintanae91ebe22009-09-29 20:44:30 -0700577 final boolean backgroundDataUsageAllowed = !mBootCompleted ||
Jim Millerd2a3a8a2009-09-15 17:34:42 -0700578 getConnectivityManager().getBackgroundDataSetting();
Fred Quintanaf892fb32009-08-27 21:32:08 -0700579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 if (extras == null) extras = new Bundle();
581
582 Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
583 if (expedited) {
584 delay = -1; // this means schedule at the front of the queue
585 }
586
Amith Yamasani04e0d262012-02-14 11:50:53 -0800587 AccountAndUser[] accounts;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700588 if (requestedAccount != null && userId != UserHandle.USER_ALL) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800589 accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 } else {
591 // if the accounts aren't configured yet then we can't support an account-less
592 // sync request
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700593 accounts = mRunningAccounts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 if (accounts.length == 0) {
595 if (isLoggable) {
596 Log.v(TAG, "scheduleSync: no accounts configured, dropping");
597 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 return;
599 }
600 }
601
602 final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700603 final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
Fred Quintana53bd2522010-02-05 15:28:12 -0800604 if (manualSync) {
605 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
606 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
607 }
608 final boolean ignoreSettings =
609 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610
611 int source;
612 if (uploadOnly) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700613 source = SyncStorageEngine.SOURCE_LOCAL;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700614 } else if (manualSync) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700615 source = SyncStorageEngine.SOURCE_USER;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700616 } else if (requestedAuthority == null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -0700617 source = SyncStorageEngine.SOURCE_POLL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 } else {
619 // this isn't strictly server, since arbitrary callers can (and do) request
620 // a non-forced two-way sync on a specific url
Dianne Hackborn231cc602009-04-27 17:10:36 -0700621 source = SyncStorageEngine.SOURCE_SERVER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 }
623
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700624 for (AccountAndUser account : accounts) {
625 // Compile a list of authorities that have sync adapters.
626 // For each authority sync each account that matches a sync adapter.
627 final HashSet<String> syncableAuthorities = new HashSet<String>();
628 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
629 mSyncAdapters.getAllServices(account.userId)) {
630 syncableAuthorities.add(syncAdapter.type.authority);
631 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700633 // if the url was specified then replace the list of authorities
634 // with just this authority or clear it if this authority isn't
635 // syncable
636 if (requestedAuthority != null) {
637 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
638 syncableAuthorities.clear();
639 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
640 }
Fred Quintana718d8a22009-04-29 17:53:20 -0700641
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700642 for (String authority : syncableAuthorities) {
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700643 int isSyncable = getIsSyncable(account.account, account.userId,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800644 authority);
Fred Quintana4a6679b2009-08-17 13:05:39 -0700645 if (isSyncable == 0) {
Fred Quintana5e787c42009-08-16 23:13:53 -0700646 continue;
647 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700648 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
649 syncAdapterInfo = mSyncAdapters.getServiceInfo(
650 SyncAdapterType.newKey(authority, account.account.type), account.userId);
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700651 if (syncAdapterInfo == null) {
652 continue;
653 }
654 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
655 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
656 if (isSyncable < 0 && isAlwaysSyncable) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800657 mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700658 isSyncable = 1;
659 }
660 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
661 continue;
662 }
663 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
664 continue;
665 }
Fred Quintanaf892fb32009-08-27 21:32:08 -0700666
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700667 // always allow if the isSyncable state is unknown
668 boolean syncAllowed =
669 (isSyncable < 0)
670 || ignoreSettings
Amith Yamasani04e0d262012-02-14 11:50:53 -0800671 || (backgroundDataUsageAllowed
672 && mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
673 && mSyncStorageEngine.getSyncAutomatically(account.account,
674 account.userId, authority));
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700675 if (!syncAllowed) {
676 if (isLoggable) {
677 Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
678 + " is not allowed, dropping request");
Fred Quintanae7424ff2009-10-14 15:59:21 -0700679 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700680 continue;
681 }
Fred Quintanae7424ff2009-10-14 15:59:21 -0700682
Amith Yamasani04e0d262012-02-14 11:50:53 -0800683 Pair<Long, Long> backoff = mSyncStorageEngine
684 .getBackoff(account.account, account.userId, authority);
685 long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account,
686 account.userId, authority);
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700687 final long backoffTime = backoff != null ? backoff.first : 0;
688 if (isSyncable < 0) {
689 Bundle newExtras = new Bundle();
690 newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
691 if (isLoggable) {
692 Log.v(TAG, "scheduleSync:"
693 + " delay " + delay
694 + ", source " + source
695 + ", account " + account
696 + ", authority " + authority
697 + ", extras " + newExtras);
Fred Quintanae0616ff2009-08-19 13:13:18 -0700698 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700699 scheduleSyncOperation(
Alon Albert57286f92012-10-09 14:21:38 -0700700 new SyncOperation(account.account, account.userId, reason, source,
701 authority, newExtras, 0, backoffTime, delayUntil,
702 allowParallelSyncs));
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700703 }
704 if (!onlyThoseWithUnkownSyncableState) {
705 if (isLoggable) {
706 Log.v(TAG, "scheduleSync:"
707 + " delay " + delay
708 + ", source " + source
709 + ", account " + account
710 + ", authority " + authority
711 + ", extras " + extras);
Fred Quintana918339a2010-10-05 14:00:39 -0700712 }
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700713 scheduleSyncOperation(
Alon Albert57286f92012-10-09 14:21:38 -0700714 new SyncOperation(account.account, account.userId, reason, source,
715 authority, extras, delay, backoffTime, delayUntil,
716 allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 }
718 }
719 }
720 }
721
Alon Albert57286f92012-10-09 14:21:38 -0700722 public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 final Bundle extras = new Bundle();
724 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
Alon Albert57286f92012-10-09 14:21:38 -0700725 scheduleSync(account, userId, reason, authority, extras, LOCAL_SYNC_DELAY,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700726 false /* onlyThoseWithUnkownSyncableState */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 }
728
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700729 public SyncAdapterType[] getSyncAdapterTypes(int userId) {
730 final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
731 serviceInfos = mSyncAdapters.getAllServices(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700732 SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
733 int i = 0;
734 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
735 types[i] = serviceInfo.type;
736 ++i;
737 }
738 return types;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 }
740
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 private void sendSyncAlarmMessage() {
742 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM");
743 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM);
744 }
745
746 private void sendCheckAlarmsMessage() {
747 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
Ashish Sharma69d95de2012-04-11 17:27:24 -0700748 mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
750 }
751
752 private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
753 SyncResult syncResult) {
754 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED");
755 Message msg = mSyncHandler.obtainMessage();
756 msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
757 msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult);
758 mSyncHandler.sendMessage(msg);
759 }
760
Amith Yamasani04e0d262012-02-14 11:50:53 -0800761 private void sendCancelSyncsMessage(final Account account, final int userId,
762 final String authority) {
Fred Quintana918339a2010-10-05 14:00:39 -0700763 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
764 Message msg = mSyncHandler.obtainMessage();
765 msg.what = SyncHandler.MESSAGE_CANCEL;
766 msg.obj = Pair.create(account, authority);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800767 msg.arg1 = userId;
Fred Quintana918339a2010-10-05 14:00:39 -0700768 mSyncHandler.sendMessage(msg);
769 }
770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 class SyncHandlerMessagePayload {
772 public final ActiveSyncContext activeSyncContext;
773 public final SyncResult syncResult;
774
775 SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) {
776 this.activeSyncContext = syncContext;
777 this.syncResult = syncResult;
778 }
779 }
780
781 class SyncAlarmIntentReceiver extends BroadcastReceiver {
782 public void onReceive(Context context, Intent intent) {
783 mHandleAlarmWakeLock.acquire();
784 sendSyncAlarmMessage();
785 }
786 }
787
Alon Albert6e079a32010-11-12 12:41:09 -0800788 private void clearBackoffSetting(SyncOperation op) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800789 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
Alon Albert6e079a32010-11-12 12:41:09 -0800790 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
791 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800792 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0);
Alon Albert6e079a32010-11-12 12:41:09 -0800793 }
794 }
795
Fred Quintana307da1a2010-01-21 14:24:20 -0800796 private void increaseBackoffSetting(SyncOperation op) {
Ashish Sharma69d95de2012-04-11 17:27:24 -0700797 // TODO: Use this function to align it to an already scheduled sync
798 // operation in the specified window
Fred Quintana307da1a2010-01-21 14:24:20 -0800799 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800
Fred Quintana307da1a2010-01-21 14:24:20 -0800801 final Pair<Long, Long> previousSettings =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800802 mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority);
Alon Albertaeeb6202010-12-09 16:14:02 -0800803 long newDelayInMs = -1;
804 if (previousSettings != null) {
805 // don't increase backoff before current backoff is expired. This will happen for op's
806 // with ignoreBackoff set.
807 if (now < previousSettings.first) {
808 if (Log.isLoggable(TAG, Log.VERBOSE)) {
809 Log.v(TAG, "Still in backoff, do not increase it. "
810 + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
811 }
812 return;
813 }
814 // Subsequent delays are the double of the previous delay
815 newDelayInMs = previousSettings.second * 2;
816 }
817 if (newDelayInMs <= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS
819 newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
820 (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 }
822
823 // Cap the delay
Jeff Sharkey625239a2012-09-26 22:03:49 -0700824 long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(),
825 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
827 if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
828 newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
829 }
830
Alon Albertc1ac7762010-10-28 13:35:55 -0700831 final long backoff = now + newDelayInMs;
832
Amith Yamasani04e0d262012-02-14 11:50:53 -0800833 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
Alon Albertc1ac7762010-10-28 13:35:55 -0700834 backoff, newDelayInMs);
835
836 op.backoff = backoff;
837 op.updateEffectiveRunTime();
838
Fred Quintana918339a2010-10-05 14:00:39 -0700839 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800840 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff);
Fred Quintana918339a2010-10-05 14:00:39 -0700841 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800842 }
843
844 private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
845 final long delayUntil = delayUntilSeconds * 1000;
846 final long absoluteNow = System.currentTimeMillis();
847 long newDelayUntilTime;
848 if (delayUntil > absoluteNow) {
849 newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow);
850 } else {
851 newDelayUntilTime = 0;
852 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800853 mSyncStorageEngine
854 .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime);
Fred Quintana918339a2010-10-05 14:00:39 -0700855 synchronized (mSyncQueue) {
856 mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
857 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 }
859
860 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -0700861 * Cancel the active sync if it matches the authority and account.
862 * @param account limit the cancelations to syncs with this account, if non-null
863 * @param authority limit the cancelations to syncs with this authority, if non-null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800865 public void cancelActiveSync(Account account, int userId, String authority) {
866 sendCancelSyncsMessage(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 }
868
869 /**
870 * Create and schedule a SyncOperation.
871 *
872 * @param syncOperation the SyncOperation to schedule
873 */
874 public void scheduleSyncOperation(SyncOperation syncOperation) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800875 boolean queueChanged;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 synchronized (mSyncQueue) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800877 queueChanged = mSyncQueue.add(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879
Fred Quintana307da1a2010-01-21 14:24:20 -0800880 if (queueChanged) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 if (Log.isLoggable(TAG, Log.VERBOSE)) {
882 Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
883 }
884 sendCheckAlarmsMessage();
885 } else {
886 if (Log.isLoggable(TAG, Log.VERBOSE)) {
887 Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
888 + syncOperation);
889 }
890 }
891 }
892
893 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -0700894 * Remove scheduled sync operations.
895 * @param account limit the removals to operations with this account, if non-null
896 * @param authority limit the removals to operations with this authority, if non-null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800898 public void clearScheduledSyncOperations(Account account, int userId, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800900 mSyncQueue.remove(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800902 mSyncStorageEngine.setBackoff(account, userId, authority,
Fred Quintana918339a2010-10-05 14:00:39 -0700903 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 }
905
Fred Quintana307da1a2010-01-21 14:24:20 -0800906 void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
908 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800909 Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 }
911
Fred Quintana53bd2522010-02-05 15:28:12 -0800912 operation = new SyncOperation(operation);
913
914 // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
915 // request. Retries of the request will always honor the backoff, so clear the
916 // flag in case we retry this request.
917 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
918 operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
919 }
920
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800921 // If this sync aborted because the internal sync loop retried too many times then
922 // don't reschedule. Otherwise we risk getting into a retry loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 // If the operation succeeded to some extent then retry immediately.
924 // If this was a two-way sync then retry soft errors with an exponential backoff.
925 // If this was an upward sync then schedule a two-way sync immediately.
926 // Otherwise do not reschedule.
Fred Quintana53bd2522010-02-05 15:28:12 -0800927 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
928 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
Fred Quintana307da1a2010-01-21 14:24:20 -0800929 + operation);
Fred Quintana918339a2010-10-05 14:00:39 -0700930 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
931 && !syncResult.syncAlreadyInProgress) {
Fred Quintana53bd2522010-02-05 15:28:12 -0800932 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
Fred Quintana307da1a2010-01-21 14:24:20 -0800933 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
934 + "encountered an error: " + operation);
Fred Quintana53bd2522010-02-05 15:28:12 -0800935 scheduleSyncOperation(operation);
Fred Quintana307da1a2010-01-21 14:24:20 -0800936 } else if (syncResult.tooManyRetries) {
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800937 Log.d(TAG, "not retrying sync operation because it retried too many times: "
Fred Quintana307da1a2010-01-21 14:24:20 -0800938 + operation);
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800939 } else if (syncResult.madeSomeProgress()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800941 Log.d(TAG, "retrying sync operation because even though it had an error "
942 + "it achieved some success");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 }
Fred Quintana53bd2522010-02-05 15:28:12 -0800944 scheduleSyncOperation(operation);
Fred Quintana8570f742010-02-18 10:32:54 -0800945 } else if (syncResult.syncAlreadyInProgress) {
946 if (isLoggable) {
947 Log.d(TAG, "retrying sync operation that failed because there was already a "
948 + "sync in progress: " + operation);
949 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800950 scheduleSyncOperation(new SyncOperation(operation.account, operation.userId,
Alon Albert57286f92012-10-09 14:21:38 -0700951 operation.reason,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800952 operation.syncSource,
Fred Quintana8570f742010-02-18 10:32:54 -0800953 operation.authority, operation.extras,
Fred Quintana918339a2010-10-05 14:00:39 -0700954 DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700955 operation.backoff, operation.delayUntil, operation.allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 } else if (syncResult.hasSoftError()) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800957 if (isLoggable) {
958 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
959 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 }
Fred Quintana53bd2522010-02-05 15:28:12 -0800961 scheduleSyncOperation(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -0800963 Log.d(TAG, "not retrying sync operation because the error is a hard error: "
964 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966 }
967
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700968 private void onUserStarting(int userId) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700969 // Make sure that accounts we're about to use are valid
970 AccountManagerService.getSingleton().validateAccounts(userId);
971
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700972 mSyncAdapters.invalidateCache(userId);
973
974 updateRunningAccounts();
975
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700976 synchronized (mSyncQueue) {
977 mSyncQueue.addPendingOperations(userId);
978 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700979
980 // Schedule sync for any accounts under started user
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700981 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700982 for (Account account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -0700983 scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
984 0 /* no delay */, true /* onlyThoseWithUnknownSyncableState */);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700985 }
986
987 sendCheckAlarmsMessage();
Alon Albert8e285552012-09-17 15:05:27 -0700988 }
Amith Yamasani13593602012-03-22 16:16:17 -0700989
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700990 private void onUserStopping(int userId) {
991 updateRunningAccounts();
992
993 cancelActiveSync(
994 null /* any account */,
995 userId,
996 null /* any authority */);
997 }
998
999 private void onUserRemoved(int userId) {
1000 updateRunningAccounts();
1001
Amith Yamasani13593602012-03-22 16:16:17 -07001002 // Clean up the storage engine database
1003 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001004 synchronized (mSyncQueue) {
1005 mSyncQueue.removeUser(userId);
1006 }
1007 }
1008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 /**
1010 * @hide
1011 */
Alon Alberteca75112010-12-08 15:02:33 -08001012 class ActiveSyncContext extends ISyncContext.Stub
1013 implements ServiceConnection, IBinder.DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 final SyncOperation mSyncOperation;
1015 final long mHistoryRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001016 ISyncAdapter mSyncAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 final long mStartTime;
1018 long mTimeoutStartTime;
Fred Quintana3ec47302010-03-10 10:08:31 -08001019 boolean mBound;
Fred Quintana918339a2010-10-05 14:00:39 -07001020 final PowerManager.WakeLock mSyncWakeLock;
1021 final int mSyncAdapterUid;
1022 SyncInfo mSyncInfo;
Alon Alberteca75112010-12-08 15:02:33 -08001023 boolean mIsLinkedToDeath = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024
Fred Quintana918339a2010-10-05 14:00:39 -07001025 /**
1026 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
1027 * sync adapter. Since this grabs the wakelock you need to be sure to call
1028 * close() when you are done with this ActiveSyncContext, whether the sync succeeded
1029 * or not.
1030 * @param syncOperation the SyncOperation we are about to sync
1031 * @param historyRowId the row in which to record the history info for this sync
1032 * @param syncAdapterUid the UID of the application that contains the sync adapter
1033 * for this sync. This is used to attribute the wakelock hold to that application.
1034 */
1035 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
1036 int syncAdapterUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 super();
Fred Quintana918339a2010-10-05 14:00:39 -07001038 mSyncAdapterUid = syncAdapterUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 mSyncOperation = syncOperation;
1040 mHistoryRowId = historyRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001041 mSyncAdapter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 mStartTime = SystemClock.elapsedRealtime();
1043 mTimeoutStartTime = mStartTime;
Fred Quintana918339a2010-10-05 14:00:39 -07001044 mSyncWakeLock = mSyncHandler.getSyncWakeLock(
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001045 mSyncOperation.account, mSyncOperation.authority);
Fred Quintana918339a2010-10-05 14:00:39 -07001046 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
1047 mSyncWakeLock.acquire();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 }
1049
1050 public void sendHeartbeat() {
Fred Quintana307da1a2010-01-21 14:24:20 -08001051 // heartbeats are no longer used
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
1053
1054 public void onFinished(SyncResult result) {
Fred Quintana918339a2010-10-05 14:00:39 -07001055 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 // include "this" in the message so that the handler can ignore it if this
1057 // ActiveSyncContext is no longer the mActiveSyncContext at message handling
1058 // time
1059 sendSyncFinishedOrCanceledMessage(this, result);
1060 }
1061
1062 public void toString(StringBuilder sb) {
1063 sb.append("startTime ").append(mStartTime)
1064 .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
1065 .append(", mHistoryRowId ").append(mHistoryRowId)
1066 .append(", syncOperation ").append(mSyncOperation);
1067 }
1068
Fred Quintana718d8a22009-04-29 17:53:20 -07001069 public void onServiceConnected(ComponentName name, IBinder service) {
1070 Message msg = mSyncHandler.obtainMessage();
1071 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
1072 msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
1073 mSyncHandler.sendMessage(msg);
1074 }
1075
1076 public void onServiceDisconnected(ComponentName name) {
1077 Message msg = mSyncHandler.obtainMessage();
1078 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
1079 msg.obj = new ServiceConnectionData(this, null);
1080 mSyncHandler.sendMessage(msg);
1081 }
1082
Dianne Hackborn41203752012-08-31 14:05:51 -07001083 boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001084 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1085 Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
1086 }
1087 Intent intent = new Intent();
1088 intent.setAction("android.content.SyncAdapter");
1089 intent.setComponent(info.componentName);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -07001090 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1091 com.android.internal.R.string.sync_binding_label);
Dianne Hackborn41203752012-08-31 14:05:51 -07001092 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1093 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
1094 null, new UserHandle(userId)));
Fred Quintana3ec47302010-03-10 10:08:31 -08001095 mBound = true;
Amith Yamasani27b89e62013-01-16 12:30:11 -08001096 final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Dianne Hackborne02c88a2011-10-28 13:58:15 -07001097 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Amith Yamasani04e0d262012-02-14 11:50:53 -08001098 | Context.BIND_ALLOW_OOM_MANAGEMENT,
Amith Yamasani27b89e62013-01-16 12:30:11 -08001099 new UserHandle(mSyncOperation.userId));
Fred Quintana3ec47302010-03-10 10:08:31 -08001100 if (!bindResult) {
1101 mBound = false;
1102 }
1103 return bindResult;
Fred Quintana718d8a22009-04-29 17:53:20 -07001104 }
1105
Fred Quintana918339a2010-10-05 14:00:39 -07001106 /**
1107 * Performs the required cleanup, which is the releasing of the wakelock and
1108 * unbinding from the sync adapter (if actually bound).
1109 */
Fred Quintana3ec47302010-03-10 10:08:31 -08001110 protected void close() {
Fred Quintana718d8a22009-04-29 17:53:20 -07001111 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1112 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
1113 }
Fred Quintana3ec47302010-03-10 10:08:31 -08001114 if (mBound) {
1115 mBound = false;
1116 mContext.unbindService(this);
1117 }
Fred Quintana918339a2010-10-05 14:00:39 -07001118 mSyncWakeLock.release();
Dianne Hackbornc24ab862011-10-18 15:55:03 -07001119 mSyncWakeLock.setWorkSource(null);
Fred Quintana718d8a22009-04-29 17:53:20 -07001120 }
1121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 @Override
1123 public String toString() {
1124 StringBuilder sb = new StringBuilder();
1125 toString(sb);
1126 return sb.toString();
1127 }
Alon Alberteca75112010-12-08 15:02:33 -08001128
1129 @Override
1130 public void binderDied() {
1131 sendSyncFinishedOrCanceledMessage(this, null);
1132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 }
1134
1135 protected void dump(FileDescriptor fd, PrintWriter pw) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001136 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1137 dumpSyncState(ipw);
1138 dumpSyncHistory(ipw);
1139 dumpSyncAdapters(ipw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
1141
Dianne Hackborn231cc602009-04-27 17:10:36 -07001142 static String formatTime(long time) {
1143 Time tobj = new Time();
1144 tobj.set(time);
1145 return tobj.format("%Y-%m-%d %H:%M:%S");
1146 }
Doug Zongker44f57472009-09-20 15:52:43 -07001147
Alon Alberte0bde332011-09-22 14:26:16 -07001148 protected void dumpSyncState(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001149 pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001150 pw.print("auto sync: ");
1151 List<UserInfo> users = getAllUsers();
1152 if (users != null) {
1153 for (UserInfo user : users) {
1154 pw.print("u" + user.id + "="
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001155 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001156 }
1157 pw.println();
1158 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001159 pw.print("memory low: "); pw.println(mStorageIsLow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001161 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001162
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001163 pw.print("accounts: ");
Fred Quintana53bd2522010-02-05 15:28:12 -08001164 if (accounts != INITIAL_ACCOUNTS_ARRAY) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001165 pw.println(accounts.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 } else {
Fred Quintana53bd2522010-02-05 15:28:12 -08001167 pw.println("not known yet");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 final long now = SystemClock.elapsedRealtime();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001170 pw.print("now: "); pw.print(now);
1171 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
Ashish Sharma69d95de2012-04-11 17:27:24 -07001172 pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000));
1173 pw.println(" (HH:MM:SS)");
Dianne Hackborn231cc602009-04-27 17:10:36 -07001174 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
1175 pw.println(" (HH:MM:SS)");
1176 pw.print("time spent syncing: ");
1177 pw.print(DateUtils.formatElapsedTime(
1178 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
1179 pw.print(" (HH:MM:SS), sync ");
1180 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
1181 pw.println("in progress");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 if (mSyncHandler.mAlarmScheduleTime != null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001183 pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
1184 pw.print(" (");
1185 pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
1186 pw.println(" (HH:MM:SS) from now)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 } else {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001188 pw.println("no alarm is scheduled (there had better not be any pending syncs)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
1190
Dianne Hackborn231cc602009-04-27 17:10:36 -07001191 pw.print("notification info: ");
Alon Alberte0bde332011-09-22 14:26:16 -07001192 final StringBuilder sb = new StringBuilder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 mSyncHandler.mSyncNotificationInfo.toString(sb);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001194 pw.println(sb.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195
Fred Quintana918339a2010-10-05 14:00:39 -07001196 pw.println();
1197 pw.println("Active Syncs: " + mActiveSyncContexts.size());
Alon Albert57286f92012-10-09 14:21:38 -07001198 final PackageManager pm = mContext.getPackageManager();
Fred Quintana918339a2010-10-05 14:00:39 -07001199 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
1200 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
1201 pw.print(" ");
1202 pw.print(DateUtils.formatElapsedTime(durationInSeconds));
1203 pw.print(" - ");
Alon Albert57286f92012-10-09 14:21:38 -07001204 pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
Fred Quintana918339a2010-10-05 14:00:39 -07001205 pw.println();
1206 }
1207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 synchronized (mSyncQueue) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001209 sb.setLength(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 mSyncQueue.dump(sb);
1211 }
Fred Quintana918339a2010-10-05 14:00:39 -07001212 pw.println();
1213 pw.print(sb.toString());
Dianne Hackborn231cc602009-04-27 17:10:36 -07001214
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001215 // join the installed sync adapter with the accounts list and emit for everything
1216 pw.println();
1217 pw.println("Sync Status");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001218 for (AccountAndUser account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -07001219 pw.printf("Account %s u%d %s\n",
1220 account.account.name, account.userId, account.account.type);
1221
1222 pw.println("=======================================================================");
1223 final PrintTable table = new PrintTable(13);
1224 table.set(0, 0,
1225 "Authority", // 0
1226 "Syncable", // 1
1227 "Enabled", // 2
1228 "Delay", // 3
1229 "Loc", // 4
1230 "Poll", // 5
1231 "Per", // 6
1232 "Serv", // 7
1233 "User", // 8
1234 "Tot", // 9
1235 "Time", // 10
1236 "Last Sync", // 11
1237 "Periodic" // 12
1238 );
1239
1240 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
1241 Lists.newArrayList();
1242 sorted.addAll(mSyncAdapters.getAllServices(account.userId));
1243 Collections.sort(sorted,
1244 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
1245 @Override
1246 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
1247 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
1248 return lhs.type.authority.compareTo(rhs.type.authority);
1249 }
1250 });
1251 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001252 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001253 continue;
1254 }
Alon Albert57286f92012-10-09 14:21:38 -07001255 int row = table.getNumRows();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001256 SyncStorageEngine.AuthorityInfo settings =
1257 mSyncStorageEngine.getOrCreateAuthority(
1258 account.account, account.userId, syncAdapterType.type.authority);
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001259 SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
Alon Albert57286f92012-10-09 14:21:38 -07001260
1261 String authority = settings.authority;
1262 if (authority.length() > 50) {
1263 authority = authority.substring(authority.length() - 50);
1264 }
1265 table.set(row, 0, authority, settings.syncable, settings.enabled);
1266 table.set(row, 4,
1267 status.numSourceLocal,
1268 status.numSourcePoll,
1269 status.numSourcePeriodic,
1270 status.numSourceServer,
1271 status.numSourceUser,
1272 status.numSyncs,
1273 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
1274
1275
1276 for (int i = 0; i < settings.periodicSyncs.size(); i++) {
1277 final Pair<Bundle, Long> pair = settings.periodicSyncs.get(0);
1278 final String period = String.valueOf(pair.second);
1279 final String extras = pair.first.size() > 0 ? pair.first.toString() : "";
1280 final String next = formatTime(status.getPeriodicSyncTime(0)
1281 + pair.second * 1000);
1282 table.set(row + i * 2, 12, period + extras);
1283 table.set(row + i * 2 + 1, 12, next);
1284 }
1285
1286 int row1 = row;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001287 if (settings.delayUntil > now) {
Alon Albert57286f92012-10-09 14:21:38 -07001288 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
1289 if (settings.backoffTime > now) {
1290 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
1291 table.set(row1++, 12, settings.backoffDelay / 1000);
1292 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001293 }
Alon Albert57286f92012-10-09 14:21:38 -07001294
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001295 if (status.lastSuccessTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001296 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
1297 + " " + "SUCCESS");
1298 table.set(row1++, 11, formatTime(status.lastSuccessTime));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001299 }
1300 if (status.lastFailureTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001301 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
1302 + " " + "FAILURE");
1303 table.set(row1++, 11, formatTime(status.lastFailureTime));
1304 //noinspection UnusedAssignment
1305 table.set(row1++, 11, status.lastFailureMesg);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001306 }
1307 }
Alon Albert57286f92012-10-09 14:21:38 -07001308 table.writeTo(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 }
1310 }
1311
Fabrice Di Megliodc2dd882011-01-26 14:31:48 -08001312 private String getLastFailureMessage(int code) {
1313 switch (code) {
1314 case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS:
1315 return "sync already in progress";
1316
1317 case ContentResolver.SYNC_ERROR_AUTHENTICATION:
1318 return "authentication error";
1319
1320 case ContentResolver.SYNC_ERROR_IO:
1321 return "I/O error";
1322
1323 case ContentResolver.SYNC_ERROR_PARSE:
1324 return "parse error";
1325
1326 case ContentResolver.SYNC_ERROR_CONFLICT:
1327 return "conflict error";
1328
1329 case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS:
1330 return "too many deletions error";
1331
1332 case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES:
1333 return "too many retries error";
1334
1335 case ContentResolver.SYNC_ERROR_INTERNAL:
1336 return "internal error";
1337
1338 default:
1339 return "unknown";
1340 }
1341 }
1342
Dianne Hackborn231cc602009-04-27 17:10:36 -07001343 private void dumpTimeSec(PrintWriter pw, long time) {
1344 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
1345 pw.print('s');
1346 }
Doug Zongker44f57472009-09-20 15:52:43 -07001347
Dianne Hackborn231cc602009-04-27 17:10:36 -07001348 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
1349 pw.print("Success ("); pw.print(ds.successCount);
1350 if (ds.successCount > 0) {
1351 pw.print(" for "); dumpTimeSec(pw, ds.successTime);
1352 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
1353 }
1354 pw.print(") Failure ("); pw.print(ds.failureCount);
1355 if (ds.failureCount > 0) {
1356 pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
1357 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
1358 }
1359 pw.println(")");
1360 }
Doug Zongker44f57472009-09-20 15:52:43 -07001361
Alon Alberte0bde332011-09-22 14:26:16 -07001362 protected void dumpSyncHistory(PrintWriter pw) {
1363 dumpRecentHistory(pw);
1364 dumpDayStatistics(pw);
1365 }
1366
1367 private void dumpRecentHistory(PrintWriter pw) {
1368 final ArrayList<SyncStorageEngine.SyncHistoryItem> items
1369 = mSyncStorageEngine.getSyncHistory();
1370 if (items != null && items.size() > 0) {
1371 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
1372 long totalElapsedTime = 0;
1373 long totalTimes = 0;
1374 final int N = items.size();
1375
1376 int maxAuthority = 0;
1377 int maxAccount = 0;
1378 for (SyncStorageEngine.SyncHistoryItem item : items) {
1379 SyncStorageEngine.AuthorityInfo authority
1380 = mSyncStorageEngine.getAuthority(item.authorityId);
1381 final String authorityName;
1382 final String accountKey;
1383 if (authority != null) {
1384 authorityName = authority.authority;
Alon Albert8e285552012-09-17 15:05:27 -07001385 accountKey = authority.account.name + "/" + authority.account.type
1386 + " u" + authority.userId;
Alon Alberte0bde332011-09-22 14:26:16 -07001387 } else {
1388 authorityName = "Unknown";
1389 accountKey = "Unknown";
1390 }
1391
1392 int length = authorityName.length();
1393 if (length > maxAuthority) {
1394 maxAuthority = length;
1395 }
1396 length = accountKey.length();
1397 if (length > maxAccount) {
1398 maxAccount = length;
1399 }
1400
1401 final long elapsedTime = item.elapsedTime;
1402 totalElapsedTime += elapsedTime;
1403 totalTimes++;
1404 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
1405 if (authoritySyncStats == null) {
1406 authoritySyncStats = new AuthoritySyncStats(authorityName);
1407 authorityMap.put(authorityName, authoritySyncStats);
1408 }
1409 authoritySyncStats.elapsedTime += elapsedTime;
1410 authoritySyncStats.times++;
1411 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
1412 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
1413 if (accountSyncStats == null) {
1414 accountSyncStats = new AccountSyncStats(accountKey);
1415 accountMap.put(accountKey, accountSyncStats);
1416 }
1417 accountSyncStats.elapsedTime += elapsedTime;
1418 accountSyncStats.times++;
1419
1420 }
1421
Alon Albert27096822012-01-11 18:06:41 -08001422 if (totalElapsedTime > 0) {
1423 pw.println();
1424 pw.printf("Detailed Statistics (Recent history): "
1425 + "%d (# of times) %ds (sync time)\n",
1426 totalTimes, totalElapsedTime / 1000);
Alon Alberte0bde332011-09-22 14:26:16 -07001427
Alon Albert27096822012-01-11 18:06:41 -08001428 final List<AuthoritySyncStats> sortedAuthorities =
1429 new ArrayList<AuthoritySyncStats>(authorityMap.values());
1430 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
Alon Albertbf976ba2011-10-03 13:06:43 -07001431 @Override
Alon Albert27096822012-01-11 18:06:41 -08001432 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
Alon Albertbf976ba2011-10-03 13:06:43 -07001433 // reverse order
1434 int compare = Integer.compare(rhs.times, lhs.times);
1435 if (compare == 0) {
1436 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
Alon Alberte0bde332011-09-22 14:26:16 -07001437 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001438 return compare;
Alon Alberte0bde332011-09-22 14:26:16 -07001439 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001440 });
Alon Albert27096822012-01-11 18:06:41 -08001441
1442 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
1443 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
1444 final char chars[] = new char[padLength];
1445 Arrays.fill(chars, '-');
1446 final String separator = new String(chars);
1447
1448 final String authorityFormat =
1449 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2);
1450 final String accountFormat =
1451 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength);
1452
1453 pw.println(separator);
1454 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
1455 String name = authoritySyncStats.name;
1456 long elapsedTime;
1457 int times;
1458 String timeStr;
1459 String timesStr;
1460
1461 elapsedTime = authoritySyncStats.elapsedTime;
1462 times = authoritySyncStats.times;
Alon Albertbf976ba2011-10-03 13:06:43 -07001463 timeStr = String.format("%ds/%d%%",
1464 elapsedTime / 1000,
1465 elapsedTime * 100 / totalElapsedTime);
1466 timesStr = String.format("%d/%d%%",
1467 times,
1468 times * 100 / totalTimes);
Alon Albert27096822012-01-11 18:06:41 -08001469 pw.printf(authorityFormat, name, timesStr, timeStr);
1470
1471 final List<AccountSyncStats> sortedAccounts =
1472 new ArrayList<AccountSyncStats>(
1473 authoritySyncStats.accountMap.values());
1474 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
1475 @Override
1476 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
1477 // reverse order
1478 int compare = Integer.compare(rhs.times, lhs.times);
1479 if (compare == 0) {
1480 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
1481 }
1482 return compare;
1483 }
1484 });
1485 for (AccountSyncStats stats: sortedAccounts) {
1486 elapsedTime = stats.elapsedTime;
1487 times = stats.times;
1488 timeStr = String.format("%ds/%d%%",
1489 elapsedTime / 1000,
1490 elapsedTime * 100 / totalElapsedTime);
1491 timesStr = String.format("%d/%d%%",
1492 times,
1493 times * 100 / totalTimes);
1494 pw.printf(accountFormat, stats.name, timesStr, timeStr);
1495 }
1496 pw.println(separator);
Alon Alberte0bde332011-09-22 14:26:16 -07001497 }
Alon Alberte0bde332011-09-22 14:26:16 -07001498 }
1499
1500 pw.println();
1501 pw.println("Recent Sync History");
Alon Albert57286f92012-10-09 14:21:38 -07001502 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n";
Alon Albertbf976ba2011-10-03 13:06:43 -07001503 final Map<String, Long> lastTimeMap = Maps.newHashMap();
Alon Albert57286f92012-10-09 14:21:38 -07001504 final PackageManager pm = mContext.getPackageManager();
Alon Alberte0bde332011-09-22 14:26:16 -07001505 for (int i = 0; i < N; i++) {
1506 SyncStorageEngine.SyncHistoryItem item = items.get(i);
1507 SyncStorageEngine.AuthorityInfo authority
1508 = mSyncStorageEngine.getAuthority(item.authorityId);
1509 final String authorityName;
1510 final String accountKey;
1511 if (authority != null) {
1512 authorityName = authority.authority;
Alon Albert8e285552012-09-17 15:05:27 -07001513 accountKey = authority.account.name + "/" + authority.account.type
1514 + " u" + authority.userId;
Alon Alberte0bde332011-09-22 14:26:16 -07001515 } else {
1516 authorityName = "Unknown";
1517 accountKey = "Unknown";
1518 }
1519 final long elapsedTime = item.elapsedTime;
1520 final Time time = new Time();
1521 final long eventTime = item.eventTime;
1522 time.set(eventTime);
1523
Alon Albertbf976ba2011-10-03 13:06:43 -07001524 final String key = authorityName + "/" + accountKey;
1525 final Long lastEventTime = lastTimeMap.get(key);
1526 final String diffString;
1527 if (lastEventTime == null) {
1528 diffString = "";
1529 } else {
1530 final long diff = (lastEventTime - eventTime) / 1000;
1531 if (diff < 60) {
1532 diffString = String.valueOf(diff);
1533 } else if (diff < 3600) {
1534 diffString = String.format("%02d:%02d", diff / 60, diff % 60);
1535 } else {
1536 final long sec = diff % 3600;
1537 diffString = String.format("%02d:%02d:%02d",
1538 diff / 3600, sec / 60, sec % 60);
1539 }
1540 }
1541 lastTimeMap.put(key, eventTime);
1542
1543 pw.printf(" #%-3d: %s %8s %5.1fs %8s",
Alon Alberte0bde332011-09-22 14:26:16 -07001544 i + 1,
1545 formatTime(eventTime),
1546 SyncStorageEngine.SOURCES[item.source],
Alon Albertbf976ba2011-10-03 13:06:43 -07001547 ((float) elapsedTime) / 1000,
1548 diffString);
Alon Albert57286f92012-10-09 14:21:38 -07001549 pw.printf(format, accountKey, authorityName,
1550 SyncOperation.reasonToString(pm, item.reason));
Alon Alberte0bde332011-09-22 14:26:16 -07001551
1552 if (item.event != SyncStorageEngine.EVENT_STOP
1553 || item.upstreamActivity != 0
1554 || item.downstreamActivity != 0) {
1555 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n",
1556 item.event,
1557 item.upstreamActivity,
1558 item.downstreamActivity);
1559 }
1560 if (item.mesg != null
1561 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
1562 pw.printf(" mesg=%s\n", item.mesg);
1563 }
1564 }
Alon Albert57286f92012-10-09 14:21:38 -07001565 pw.println();
1566 pw.println("Recent Sync History Extras");
1567 for (int i = 0; i < N; i++) {
1568 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
1569 final Bundle extras = item.extras;
1570 if (extras == null || extras.size() == 0) {
1571 continue;
1572 }
1573 final SyncStorageEngine.AuthorityInfo authority
1574 = mSyncStorageEngine.getAuthority(item.authorityId);
1575 final String authorityName;
1576 final String accountKey;
1577 if (authority != null) {
1578 authorityName = authority.authority;
1579 accountKey = authority.account.name + "/" + authority.account.type
1580 + " u" + authority.userId;
1581 } else {
1582 authorityName = "Unknown";
1583 accountKey = "Unknown";
1584 }
1585 final Time time = new Time();
1586 final long eventTime = item.eventTime;
1587 time.set(eventTime);
1588
1589 pw.printf(" #%-3d: %s %8s ",
1590 i + 1,
1591 formatTime(eventTime),
1592 SyncStorageEngine.SOURCES[item.source]);
1593
1594 pw.printf(format, accountKey, authorityName, extras);
1595 }
Alon Alberte0bde332011-09-22 14:26:16 -07001596 }
1597 }
1598
1599 private void dumpDayStatistics(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001600 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
1601 if (dses != null && dses[0] != null) {
1602 pw.println();
1603 pw.println("Sync Statistics");
1604 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]);
1605 int today = dses[0].day;
1606 int i;
1607 SyncStorageEngine.DayStats ds;
Doug Zongker44f57472009-09-20 15:52:43 -07001608
Dianne Hackborn231cc602009-04-27 17:10:36 -07001609 // Print each day in the current week.
1610 for (i=1; i<=6 && i < dses.length; i++) {
1611 ds = dses[i];
1612 if (ds == null) break;
1613 int delta = today-ds.day;
1614 if (delta > 6) break;
Doug Zongker44f57472009-09-20 15:52:43 -07001615
Dianne Hackborn231cc602009-04-27 17:10:36 -07001616 pw.print(" Day-"); pw.print(delta); pw.print(": ");
1617 dumpDayStatistic(pw, ds);
1618 }
Doug Zongker44f57472009-09-20 15:52:43 -07001619
Dianne Hackborn231cc602009-04-27 17:10:36 -07001620 // Aggregate all following days into weeks and print totals.
1621 int weekDay = today;
1622 while (i < dses.length) {
1623 SyncStorageEngine.DayStats aggr = null;
1624 weekDay -= 7;
1625 while (i < dses.length) {
1626 ds = dses[i];
1627 if (ds == null) {
1628 i = dses.length;
1629 break;
1630 }
1631 int delta = weekDay-ds.day;
1632 if (delta > 6) break;
1633 i++;
Doug Zongker44f57472009-09-20 15:52:43 -07001634
Dianne Hackborn231cc602009-04-27 17:10:36 -07001635 if (aggr == null) {
1636 aggr = new SyncStorageEngine.DayStats(weekDay);
1637 }
1638 aggr.successCount += ds.successCount;
1639 aggr.successTime += ds.successTime;
1640 aggr.failureCount += ds.failureCount;
1641 aggr.failureTime += ds.failureTime;
1642 }
1643 if (aggr != null) {
1644 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": ");
1645 dumpDayStatistic(pw, aggr);
1646 }
1647 }
1648 }
Alon Alberte0bde332011-09-22 14:26:16 -07001649 }
Doug Zongker44f57472009-09-20 15:52:43 -07001650
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001651 private void dumpSyncAdapters(IndentingPrintWriter pw) {
1652 pw.println();
1653 final List<UserInfo> users = getAllUsers();
1654 if (users != null) {
1655 for (UserInfo user : users) {
1656 pw.println("Sync adapters for " + user + ":");
1657 pw.increaseIndent();
1658 for (RegisteredServicesCache.ServiceInfo<?> info :
1659 mSyncAdapters.getAllServices(user.id)) {
1660 pw.println(info);
1661 }
1662 pw.decreaseIndent();
1663 pw.println();
1664 }
1665 }
1666 }
1667
Alon Alberte0bde332011-09-22 14:26:16 -07001668 private static class AuthoritySyncStats {
1669 String name;
1670 long elapsedTime;
1671 int times;
1672 Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
1673
1674 private AuthoritySyncStats(String name) {
1675 this.name = name;
1676 }
1677 }
1678
1679 private static class AccountSyncStats {
1680 String name;
1681 long elapsedTime;
1682 int times;
1683
1684 private AccountSyncStats(String name) {
1685 this.name = name;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001686 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 }
1688
1689 /**
1690 * A helper object to keep track of the time we have spent syncing since the last boot
1691 */
1692 private class SyncTimeTracker {
1693 /** True if a sync was in progress on the most recent call to update() */
1694 boolean mLastWasSyncing = false;
1695 /** Used to track when lastWasSyncing was last set */
1696 long mWhenSyncStarted = 0;
1697 /** The cumulative time we have spent syncing */
1698 private long mTimeSpentSyncing;
1699
1700 /** Call to let the tracker know that the sync state may have changed */
1701 public synchronized void update() {
Fred Quintana918339a2010-10-05 14:00:39 -07001702 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 if (isSyncInProgress == mLastWasSyncing) return;
1704 final long now = SystemClock.elapsedRealtime();
1705 if (isSyncInProgress) {
1706 mWhenSyncStarted = now;
1707 } else {
1708 mTimeSpentSyncing += now - mWhenSyncStarted;
1709 }
1710 mLastWasSyncing = isSyncInProgress;
1711 }
1712
1713 /** Get how long we have been syncing, in ms */
1714 public synchronized long timeSpentSyncing() {
1715 if (!mLastWasSyncing) return mTimeSpentSyncing;
1716
1717 final long now = SystemClock.elapsedRealtime();
1718 return mTimeSpentSyncing + (now - mWhenSyncStarted);
1719 }
1720 }
1721
Fred Quintana718d8a22009-04-29 17:53:20 -07001722 class ServiceConnectionData {
1723 public final ActiveSyncContext activeSyncContext;
1724 public final ISyncAdapter syncAdapter;
1725 ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
1726 this.activeSyncContext = activeSyncContext;
1727 this.syncAdapter = syncAdapter;
1728 }
1729 }
1730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 /**
1732 * Handles SyncOperation Messages that are posted to the associated
1733 * HandlerThread.
1734 */
1735 class SyncHandler extends Handler {
1736 // Messages that can be sent on mHandler
1737 private static final int MESSAGE_SYNC_FINISHED = 1;
1738 private static final int MESSAGE_SYNC_ALARM = 2;
1739 private static final int MESSAGE_CHECK_ALARMS = 3;
Fred Quintana718d8a22009-04-29 17:53:20 -07001740 private static final int MESSAGE_SERVICE_CONNECTED = 4;
1741 private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
Fred Quintana918339a2010-10-05 14:00:39 -07001742 private static final int MESSAGE_CANCEL = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743
1744 public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
1745 private Long mAlarmScheduleTime = null;
1746 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001747 private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks =
Fred Quintanab3029c32010-04-06 13:27:12 -07001748 Maps.newHashMap();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749
Fred Quintanae91ebe22009-09-29 20:44:30 -07001750 private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001751
Fred Quintanae91ebe22009-09-29 20:44:30 -07001752 public void onBootCompleted() {
1753 mBootCompleted = true;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001754
Jeff Sharkey8f55d112012-10-11 11:00:21 -07001755 doDatabaseCleanup();
1756
Fred Quintanae91ebe22009-09-29 20:44:30 -07001757 if (mReadyToRunLatch != null) {
1758 mReadyToRunLatch.countDown();
1759 }
1760 }
1761
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001762 private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) {
1763 final Pair<Account, String> wakeLockKey = Pair.create(account, authority);
Fred Quintanab3029c32010-04-06 13:27:12 -07001764 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
1765 if (wakeLock == null) {
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001766 final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + account;
Fred Quintanab3029c32010-04-06 13:27:12 -07001767 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
1768 wakeLock.setReferenceCounted(false);
1769 mWakeLocks.put(wakeLockKey, wakeLock);
1770 }
1771 return wakeLock;
1772 }
1773
Fred Quintanae91ebe22009-09-29 20:44:30 -07001774 private void waitUntilReadyToRun() {
1775 CountDownLatch latch = mReadyToRunLatch;
1776 if (latch != null) {
1777 while (true) {
1778 try {
1779 latch.await();
1780 mReadyToRunLatch = null;
1781 return;
1782 } catch (InterruptedException e) {
1783 Thread.currentThread().interrupt();
1784 }
1785 }
1786 }
1787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 /**
1789 * Used to keep track of whether a sync notification is active and who it is for.
1790 */
1791 class SyncNotificationInfo {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 // true iff the notification manager has been asked to send the notification
1793 public boolean isActive = false;
1794
1795 // Set when we transition from not running a sync to running a sync, and cleared on
1796 // the opposite transition.
1797 public Long startTime = null;
1798
1799 public void toString(StringBuilder sb) {
Fred Quintana918339a2010-10-05 14:00:39 -07001800 sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 }
1802
1803 @Override
1804 public String toString() {
1805 StringBuilder sb = new StringBuilder();
1806 toString(sb);
1807 return sb.toString();
1808 }
1809 }
1810
1811 public SyncHandler(Looper looper) {
1812 super(looper);
1813 }
1814
1815 public void handleMessage(Message msg) {
Fred Quintana918339a2010-10-05 14:00:39 -07001816 long earliestFuturePollTime = Long.MAX_VALUE;
1817 long nextPendingSyncTime = Long.MAX_VALUE;
Subir Jhanb753c57b2011-01-10 12:09:26 -08001818
1819 // Setting the value here instead of a method because we want the dumpsys logs
1820 // to have the most recent value used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 try {
Fred Quintanae91ebe22009-09-29 20:44:30 -07001822 waitUntilReadyToRun();
Alon Albert1bad83a2011-02-16 10:29:56 -08001823 mDataConnectionIsConnected = readDataConnectionState();
Fred Quintana918339a2010-10-05 14:00:39 -07001824 mSyncManagerWakeLock.acquire();
Fred Quintana77c560f2010-03-29 22:20:26 -07001825 // Always do this first so that we be sure that any periodic syncs that
1826 // are ready to run have been converted into pending syncs. This allows the
1827 // logic that considers the next steps to take based on the set of pending syncs
1828 // to also take into account the periodic syncs.
1829 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 switch (msg.what) {
Fred Quintana918339a2010-10-05 14:00:39 -07001831 case SyncHandler.MESSAGE_CANCEL: {
1832 Pair<Account, String> payload = (Pair<Account, String>)msg.obj;
1833 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1834 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
1835 + payload.first + ", " + payload.second);
1836 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001837 cancelActiveSyncLocked(payload.first, msg.arg1, payload.second);
Fred Quintana918339a2010-10-05 14:00:39 -07001838 nextPendingSyncTime = maybeStartNextSyncLocked();
1839 break;
1840 }
1841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 case SyncHandler.MESSAGE_SYNC_FINISHED:
1843 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1844 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
1845 }
1846 SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj;
Fred Quintana918339a2010-10-05 14:00:39 -07001847 if (!isSyncStillActive(payload.activeSyncContext)) {
1848 Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
1849 + "sync is no longer active: "
1850 + payload.activeSyncContext);
1851 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 }
Fred Quintana918339a2010-10-05 14:00:39 -07001853 runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854
Fred Quintana918339a2010-10-05 14:00:39 -07001855 // since a sync just finished check if it is time to start a new sync
1856 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 break;
1858
Fred Quintana718d8a22009-04-29 17:53:20 -07001859 case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
1860 ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
1861 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1862 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07001863 + msgData.activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001864 }
1865 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07001866 if (isSyncStillActive(msgData.activeSyncContext)) {
1867 runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
Fred Quintana718d8a22009-04-29 17:53:20 -07001868 }
1869 break;
1870 }
1871
1872 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
Fred Quintana918339a2010-10-05 14:00:39 -07001873 final ActiveSyncContext currentSyncContext =
1874 ((ServiceConnectionData)msg.obj).activeSyncContext;
Fred Quintana718d8a22009-04-29 17:53:20 -07001875 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1876 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07001877 + currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001878 }
1879 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07001880 if (isSyncStillActive(currentSyncContext)) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001881 // cancel the sync if we have a syncadapter, which means one is
1882 // outstanding
Fred Quintana918339a2010-10-05 14:00:39 -07001883 if (currentSyncContext.mSyncAdapter != null) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001884 try {
Fred Quintana918339a2010-10-05 14:00:39 -07001885 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001886 } catch (RemoteException e) {
1887 // we don't need to retry this in this case
1888 }
1889 }
1890
1891 // pretend that the sync failed with an IOException,
1892 // which is a soft error
1893 SyncResult syncResult = new SyncResult();
1894 syncResult.stats.numIoExceptions++;
Fred Quintana918339a2010-10-05 14:00:39 -07001895 runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001896
Fred Quintana918339a2010-10-05 14:00:39 -07001897 // since a sync just finished check if it is time to start a new sync
1898 nextPendingSyncTime = maybeStartNextSyncLocked();
Fred Quintana718d8a22009-04-29 17:53:20 -07001899 }
1900
1901 break;
1902 }
1903
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 case SyncHandler.MESSAGE_SYNC_ALARM: {
1905 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
1906 if (isLoggable) {
1907 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM");
1908 }
1909 mAlarmScheduleTime = null;
1910 try {
Fred Quintana918339a2010-10-05 14:00:39 -07001911 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001912 } finally {
1913 mHandleAlarmWakeLock.release();
1914 }
1915 break;
1916 }
1917
1918 case SyncHandler.MESSAGE_CHECK_ALARMS:
1919 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1920 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
1921 }
Fred Quintana918339a2010-10-05 14:00:39 -07001922 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 break;
1924 }
1925 } finally {
Fred Quintana918339a2010-10-05 14:00:39 -07001926 manageSyncNotificationLocked();
1927 manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001928 mSyncTimeTracker.update();
Fred Quintana918339a2010-10-05 14:00:39 -07001929 mSyncManagerWakeLock.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 }
1931 }
1932
Fred Quintana77c560f2010-03-29 22:20:26 -07001933 /**
1934 * Turn any periodic sync operations that are ready to run into pending sync operations.
1935 * @return the desired start time of the earliest future periodic sync operation,
1936 * in milliseconds since boot
1937 */
Fred Quintana918339a2010-10-05 14:00:39 -07001938 private long scheduleReadyPeriodicSyncs() {
Fred Quintana77c560f2010-03-29 22:20:26 -07001939 final boolean backgroundDataUsageAllowed =
1940 getConnectivityManager().getBackgroundDataSetting();
Fred Quintana918339a2010-10-05 14:00:39 -07001941 long earliestFuturePollTime = Long.MAX_VALUE;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001942 if (!backgroundDataUsageAllowed) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001943 return earliestFuturePollTime;
1944 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001945
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001946 AccountAndUser[] accounts = mRunningAccounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001947
Fred Quintana77c560f2010-03-29 22:20:26 -07001948 final long nowAbsolute = System.currentTimeMillis();
Ashish Sharma69d95de2012-04-11 17:27:24 -07001949 final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
1950 ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
1951
Fred Quintana77c560f2010-03-29 22:20:26 -07001952 ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
1953 for (SyncStorageEngine.AuthorityInfo info : infos) {
1954 // skip the sync if the account of this operation no longer exists
Amith Yamasani04e0d262012-02-14 11:50:53 -08001955 if (!containsAccountAndUser(accounts, info.account, info.userId)) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001956 continue;
1957 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001958
Amith Yamasani04e0d262012-02-14 11:50:53 -08001959 if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId)
1960 || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId,
1961 info.authority)) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001962 continue;
1963 }
1964
Amith Yamasani9422bdc2013-04-10 16:58:19 -07001965 if (getIsSyncable(info.account, info.userId, info.authority)
Amith Yamasani04e0d262012-02-14 11:50:53 -08001966 == 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001967 continue;
1968 }
1969
1970 SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info);
1971 for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) {
1972 final Bundle extras = info.periodicSyncs.get(i).first;
Ashish Sharma69d95de2012-04-11 17:27:24 -07001973 final Long periodInMillis = info.periodicSyncs.get(i).second * 1000;
Ashish Sharma59dc7a82013-06-10 11:25:51 -07001974 // Skip if the period is invalid
1975 if (periodInMillis <= 0) {
1976 continue;
1977 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001978 // find when this periodic sync was last scheduled to run
1979 final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
Ashish Sharma69d95de2012-04-11 17:27:24 -07001980
1981 long remainingMillis
1982 = periodInMillis - (shiftedNowAbsolute % periodInMillis);
1983
1984 /*
1985 * Sync scheduling strategy:
1986 * Set the next periodic sync based on a random offset (in seconds).
1987 *
1988 * Also sync right now if any of the following cases hold
1989 * and mark it as having been scheduled
1990 *
1991 * Case 1: This sync is ready to run now.
1992 * Case 2: If the lastPollTimeAbsolute is in the future,
1993 * sync now and reinitialize. This can happen for
1994 * example if the user changed the time, synced and
1995 * changed back.
1996 * Case 3: If we failed to sync at the last scheduled time
1997 */
1998 if (remainingMillis == periodInMillis // Case 1
1999 || lastPollTimeAbsolute > nowAbsolute // Case 2
2000 || (nowAbsolute - lastPollTimeAbsolute
2001 >= periodInMillis)) { // Case 3
2002 // Sync now
Amith Yamasani04e0d262012-02-14 11:50:53 -08002003 final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
2004 info.account, info.userId, info.authority);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002005 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2006 syncAdapterInfo = mSyncAdapters.getServiceInfo(
2007 SyncAdapterType.newKey(info.authority, info.account.type),
2008 info.userId);
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002009 if (syncAdapterInfo == null) {
2010 continue;
2011 }
Fred Quintana77c560f2010-03-29 22:20:26 -07002012 scheduleSyncOperation(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002013 new SyncOperation(info.account, info.userId,
Alon Albert57286f92012-10-09 14:21:38 -07002014 SyncOperation.REASON_PERIODIC,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002015 SyncStorageEngine.SOURCE_PERIODIC,
Fred Quintana918339a2010-10-05 14:00:39 -07002016 info.authority, extras, 0 /* delay */,
2017 backoff != null ? backoff.first : 0,
2018 mSyncStorageEngine.getDelayUntilTime(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002019 info.account, info.userId, info.authority),
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002020 syncAdapterInfo.type.allowParallelSyncs()));
Fred Quintana77c560f2010-03-29 22:20:26 -07002021 status.setPeriodicSyncTime(i, nowAbsolute);
Ashish Sharma69d95de2012-04-11 17:27:24 -07002022 }
2023 // Compute when this periodic sync should next run
2024 final long nextPollTimeAbsolute = nowAbsolute + remainingMillis;
2025
2026 // remember this time if it is earlier than earliestFuturePollTime
2027 if (nextPollTimeAbsolute < earliestFuturePollTime) {
2028 earliestFuturePollTime = nextPollTimeAbsolute;
Fred Quintana77c560f2010-03-29 22:20:26 -07002029 }
2030 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002031 }
2032
Fred Quintana918339a2010-10-05 14:00:39 -07002033 if (earliestFuturePollTime == Long.MAX_VALUE) {
2034 return Long.MAX_VALUE;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002035 }
2036
Fred Quintana77c560f2010-03-29 22:20:26 -07002037 // convert absolute time to elapsed time
2038 return SystemClock.elapsedRealtime()
2039 + ((earliestFuturePollTime < nowAbsolute)
2040 ? 0
2041 : (earliestFuturePollTime - nowAbsolute));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002042 }
2043
Fred Quintana918339a2010-10-05 14:00:39 -07002044 private long maybeStartNextSyncLocked() {
2045 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2046 if (isLoggable) Log.v(TAG, "maybeStartNextSync");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047
2048 // If we aren't ready to run (e.g. the data connection is down), get out.
2049 if (!mDataConnectionIsConnected) {
2050 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002051 Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052 }
Fred Quintana918339a2010-10-05 14:00:39 -07002053 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002054 }
2055
2056 if (mStorageIsLow) {
2057 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002058 Log.v(TAG, "maybeStartNextSync: memory low, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059 }
Fred Quintana918339a2010-10-05 14:00:39 -07002060 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002061 }
2062
2063 // If the accounts aren't known yet then we aren't ready to run. We will be kicked
2064 // when the account lookup request does complete.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002065 AccountAndUser[] accounts = mRunningAccounts;
Fred Quintana53bd2522010-02-05 15:28:12 -08002066 if (accounts == INITIAL_ACCOUNTS_ARRAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002068 Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 }
Fred Quintana918339a2010-10-05 14:00:39 -07002070 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002071 }
2072
2073 // Otherwise consume SyncOperations from the head of the SyncQueue until one is
2074 // found that is runnable (not disabled, etc). If that one is ready to run then
2075 // start it, otherwise just get out.
Fred Quintanaf892fb32009-08-27 21:32:08 -07002076 final boolean backgroundDataUsageAllowed =
2077 getConnectivityManager().getBackgroundDataSetting();
Fred Quintana77c560f2010-03-29 22:20:26 -07002078
Fred Quintana918339a2010-10-05 14:00:39 -07002079 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002080
Fred Quintana918339a2010-10-05 14:00:39 -07002081 // will be set to the next time that a sync should be considered for running
2082 long nextReadyToRunTime = Long.MAX_VALUE;
2083
2084 // order the sync queue, dropping syncs that are not allowed
2085 ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
2086 synchronized (mSyncQueue) {
2087 if (isLoggable) {
2088 Log.v(TAG, "build the operation array, syncQueue size is "
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002089 + mSyncQueue.getOperations().size());
Fred Quintana918339a2010-10-05 14:00:39 -07002090 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002091 final Iterator<SyncOperation> operationIterator = mSyncQueue.getOperations()
2092 .iterator();
Alon Albert8e285552012-09-17 15:05:27 -07002093
2094 final ActivityManager activityManager
2095 = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
2096 final Set<Integer> removedUsers = Sets.newHashSet();
Fred Quintana918339a2010-10-05 14:00:39 -07002097 while (operationIterator.hasNext()) {
2098 final SyncOperation op = operationIterator.next();
Fred Quintana307da1a2010-01-21 14:24:20 -08002099
Fred Quintana77c560f2010-03-29 22:20:26 -07002100 // drop the sync if the account of this operation no longer exists
Amith Yamasani04e0d262012-02-14 11:50:53 -08002101 if (!containsAccountAndUser(accounts, op.account, op.userId)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002102 operationIterator.remove();
2103 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
Fred Quintana77c560f2010-03-29 22:20:26 -07002104 continue;
2105 }
2106
Fred Quintana918339a2010-10-05 14:00:39 -07002107 // drop this sync request if it isn't syncable
Amith Yamasani9422bdc2013-04-10 16:58:19 -07002108 int syncableState = getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002109 op.account, op.userId, op.authority);
Fred Quintana77c560f2010-03-29 22:20:26 -07002110 if (syncableState == 0) {
Fred Quintana918339a2010-10-05 14:00:39 -07002111 operationIterator.remove();
2112 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
2113 continue;
2114 }
2115
Alon Albert8e285552012-09-17 15:05:27 -07002116 // if the user in not running, drop the request
2117 if (!activityManager.isUserRunning(op.userId)) {
2118 final UserInfo userInfo = mUserManager.getUserInfo(op.userId);
2119 if (userInfo == null) {
2120 removedUsers.add(op.userId);
2121 }
2122 continue;
2123 }
2124
Fred Quintana918339a2010-10-05 14:00:39 -07002125 // if the next run time is in the future, meaning there are no syncs ready
2126 // to run, return the time
2127 if (op.effectiveRunTime > now) {
2128 if (nextReadyToRunTime > op.effectiveRunTime) {
2129 nextReadyToRunTime = op.effectiveRunTime;
2130 }
Fred Quintana77c560f2010-03-29 22:20:26 -07002131 continue;
2132 }
2133
Jeff Sharkey34821882011-07-13 10:44:07 -07002134 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2135 syncAdapterInfo = mSyncAdapters.getServiceInfo(
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002136 SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
Jeff Sharkey34821882011-07-13 10:44:07 -07002137
2138 // only proceed if network is connected for requesting UID
2139 final boolean uidNetworkConnected;
2140 if (syncAdapterInfo != null) {
2141 final NetworkInfo networkInfo = getConnectivityManager()
2142 .getActiveNetworkInfoForUid(syncAdapterInfo.uid);
2143 uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
2144 } else {
2145 uidNetworkConnected = false;
2146 }
2147
2148 // skip the sync if it isn't manual, and auto sync or
2149 // background data usage is disabled or network is
2150 // disconnected for the target UID.
Fred Quintana77c560f2010-03-29 22:20:26 -07002151 if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
2152 && (syncableState > 0)
Amith Yamasani04e0d262012-02-14 11:50:53 -08002153 && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId)
Fred Quintana77c560f2010-03-29 22:20:26 -07002154 || !backgroundDataUsageAllowed
Jeff Sharkey34821882011-07-13 10:44:07 -07002155 || !uidNetworkConnected
Fred Quintana77c560f2010-03-29 22:20:26 -07002156 || !mSyncStorageEngine.getSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002157 op.account, op.userId, op.authority))) {
Fred Quintana918339a2010-10-05 14:00:39 -07002158 operationIterator.remove();
2159 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
Fred Quintana307da1a2010-01-21 14:24:20 -08002160 continue;
2161 }
2162
Fred Quintana918339a2010-10-05 14:00:39 -07002163 operations.add(op);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002164 }
Alon Albert8e285552012-09-17 15:05:27 -07002165 for (Integer user : removedUsers) {
2166 // if it's still removed
2167 if (mUserManager.getUserInfo(user) == null) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002168 onUserRemoved(user);
Alon Albert8e285552012-09-17 15:05:27 -07002169 }
2170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002171 }
2172
Fred Quintana918339a2010-10-05 14:00:39 -07002173 // find the next operation to dispatch, if one is ready
2174 // iterate from the top, keep issuing (while potentially cancelling existing syncs)
2175 // until the quotas are filled.
2176 // once the quotas are filled iterate once more to find when the next one would be
2177 // (also considering pre-emption reasons).
2178 if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
2179 Collections.sort(operations);
2180 if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
2181 for (int i = 0, N = operations.size(); i < N; i++) {
2182 final SyncOperation candidate = operations.get(i);
2183 final boolean candidateIsInitialization = candidate.isInitialization();
2184
2185 int numInit = 0;
2186 int numRegular = 0;
2187 ActiveSyncContext conflict = null;
2188 ActiveSyncContext longRunning = null;
2189 ActiveSyncContext toReschedule = null;
Fred Quintanadc475562012-05-04 15:51:54 -07002190 ActiveSyncContext oldestNonExpeditedRegular = null;
Fred Quintana918339a2010-10-05 14:00:39 -07002191
2192 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2193 final SyncOperation activeOp = activeSyncContext.mSyncOperation;
2194 if (activeOp.isInitialization()) {
2195 numInit++;
2196 } else {
2197 numRegular++;
Fred Quintanadc475562012-05-04 15:51:54 -07002198 if (!activeOp.isExpedited()) {
2199 if (oldestNonExpeditedRegular == null
2200 || (oldestNonExpeditedRegular.mStartTime
2201 > activeSyncContext.mStartTime)) {
2202 oldestNonExpeditedRegular = activeSyncContext;
2203 }
2204 }
Fred Quintana918339a2010-10-05 14:00:39 -07002205 }
2206 if (activeOp.account.type.equals(candidate.account.type)
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002207 && activeOp.authority.equals(candidate.authority)
Amith Yamasani04e0d262012-02-14 11:50:53 -08002208 && activeOp.userId == candidate.userId
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002209 && (!activeOp.allowParallelSyncs
2210 || activeOp.account.name.equals(candidate.account.name))) {
Fred Quintana918339a2010-10-05 14:00:39 -07002211 conflict = activeSyncContext;
2212 // don't break out since we want to do a full count of the varieties
2213 } else {
2214 if (candidateIsInitialization == activeOp.isInitialization()
2215 && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
2216 longRunning = activeSyncContext;
2217 // don't break out since we want to do a full count of the varieties
2218 }
2219 }
2220 }
2221
2222 if (isLoggable) {
2223 Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
2224 Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
2225 Log.v(TAG, " longRunning: " + longRunning);
2226 Log.v(TAG, " conflict: " + conflict);
Fred Quintanadc475562012-05-04 15:51:54 -07002227 Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002228 }
2229
Fred Quintanadc475562012-05-04 15:51:54 -07002230 final boolean roomAvailable = candidateIsInitialization
2231 ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
2232 : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
2233
Fred Quintana918339a2010-10-05 14:00:39 -07002234 if (conflict != null) {
2235 if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
2236 && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
2237 toReschedule = conflict;
2238 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2239 Log.v(TAG, "canceling and rescheduling sync since an initialization "
2240 + "takes higher priority, " + conflict);
2241 }
2242 } else if (candidate.expedited && !conflict.mSyncOperation.expedited
2243 && (candidateIsInitialization
2244 == conflict.mSyncOperation.isInitialization())) {
2245 toReschedule = conflict;
2246 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2247 Log.v(TAG, "canceling and rescheduling sync since an expedited "
2248 + "takes higher priority, " + conflict);
2249 }
2250 } else {
2251 continue;
2252 }
Fred Quintanadc475562012-05-04 15:51:54 -07002253 } else if (roomAvailable) {
2254 // dispatch candidate
2255 } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null
2256 && !candidateIsInitialization) {
2257 // We found an active, non-expedited regular sync. We also know that the
2258 // candidate doesn't conflict with this active sync since conflict
2259 // is null. Reschedule the active sync and start the candidate.
2260 toReschedule = oldestNonExpeditedRegular;
2261 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2262 Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, "
2263 + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002264 }
Fred Quintanadc475562012-05-04 15:51:54 -07002265 } else if (longRunning != null
2266 && (candidateIsInitialization
2267 == longRunning.mSyncOperation.isInitialization())) {
2268 // We found an active, long-running sync. Reschedule the active
2269 // sync and start the candidate.
2270 toReschedule = longRunning;
2271 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2272 Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
2273 + longRunning);
2274 }
2275 } else {
2276 // we were unable to find or make space to run this candidate, go on to
2277 // the next one
2278 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002279 }
2280
2281 if (toReschedule != null) {
2282 runSyncFinishedOrCanceledLocked(null, toReschedule);
2283 scheduleSyncOperation(toReschedule.mSyncOperation);
2284 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002285 synchronized (mSyncQueue) {
Fred Quintana918339a2010-10-05 14:00:39 -07002286 mSyncQueue.remove(candidate);
2287 }
Fred Quintana87b14662011-09-12 10:32:55 -07002288 dispatchSyncOperation(candidate);
Fred Quintana918339a2010-10-05 14:00:39 -07002289 }
2290
2291 return nextReadyToRunTime;
2292 }
2293
Fred Quintana87b14662011-09-12 10:32:55 -07002294 private boolean dispatchSyncOperation(SyncOperation op) {
Fred Quintana918339a2010-10-05 14:00:39 -07002295 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002296 Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
Fred Quintana918339a2010-10-05 14:00:39 -07002297 Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
2298 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
2299 Log.v(TAG, syncContext.toString());
2300 }
2301 }
2302
Fred Quintana718d8a22009-04-29 17:53:20 -07002303 // connect to the sync adapter
Fred Quintana4a6679b2009-08-17 13:05:39 -07002304 SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002305 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2306 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId);
Fred Quintana718d8a22009-04-29 17:53:20 -07002307 if (syncAdapterInfo == null) {
Fred Quintana7620f1a2010-03-16 15:58:44 -07002308 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
2309 + ", removing settings for it");
Amith Yamasani04e0d262012-02-14 11:50:53 -08002310 mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority);
Fred Quintana87b14662011-09-12 10:32:55 -07002311 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 }
2313
Fred Quintana718d8a22009-04-29 17:53:20 -07002314 ActiveSyncContext activeSyncContext =
Fred Quintana918339a2010-10-05 14:00:39 -07002315 new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
2316 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
2317 mActiveSyncContexts.add(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002318 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002319 Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002320 }
Dianne Hackborn41203752012-08-31 14:05:51 -07002321 if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
Fred Quintana718d8a22009-04-29 17:53:20 -07002322 Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
Fred Quintana918339a2010-10-05 14:00:39 -07002323 closeActiveSyncContext(activeSyncContext);
Fred Quintana87b14662011-09-12 10:32:55 -07002324 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -07002325 }
2326
Fred Quintana87b14662011-09-12 10:32:55 -07002327 return true;
Fred Quintana718d8a22009-04-29 17:53:20 -07002328 }
2329
Alon Alberteca75112010-12-08 15:02:33 -08002330 private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext,
Fred Quintana918339a2010-10-05 14:00:39 -07002331 ISyncAdapter syncAdapter) {
2332 activeSyncContext.mSyncAdapter = syncAdapter;
2333 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
Fred Quintana718d8a22009-04-29 17:53:20 -07002334 try {
Alon Alberteca75112010-12-08 15:02:33 -08002335 activeSyncContext.mIsLinkedToDeath = true;
2336 syncAdapter.asBinder().linkToDeath(activeSyncContext, 0);
2337
Fred Quintana918339a2010-10-05 14:00:39 -07002338 syncAdapter.startSync(activeSyncContext, syncOperation.authority,
Fred Quintana21bb0de2009-06-16 10:24:58 -07002339 syncOperation.account, syncOperation.extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002340 } catch (RemoteException remoteExc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002341 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
2342 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002343 increaseBackoffSetting(syncOperation);
2344 scheduleSyncOperation(new SyncOperation(syncOperation));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002345 } catch (RuntimeException exc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002346 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002347 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002349 }
2350
Amith Yamasani04e0d262012-02-14 11:50:53 -08002351 private void cancelActiveSyncLocked(Account account, int userId, String authority) {
Fred Quintana918339a2010-10-05 14:00:39 -07002352 ArrayList<ActiveSyncContext> activeSyncs =
2353 new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
2354 for (ActiveSyncContext activeSyncContext : activeSyncs) {
2355 if (activeSyncContext != null) {
Alon Albert8e285552012-09-17 15:05:27 -07002356 // if an account was specified then only cancel the sync if it matches
Fred Quintana918339a2010-10-05 14:00:39 -07002357 if (account != null) {
2358 if (!account.equals(activeSyncContext.mSyncOperation.account)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002359 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002360 }
2361 }
Alon Albert8e285552012-09-17 15:05:27 -07002362 // if an authority was specified then only cancel the sync if it matches
Fred Quintana918339a2010-10-05 14:00:39 -07002363 if (authority != null) {
2364 if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002365 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002366 }
2367 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002368 // check if the userid matches
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002369 if (userId != UserHandle.USER_ALL
Amith Yamasani04e0d262012-02-14 11:50:53 -08002370 && userId != activeSyncContext.mSyncOperation.userId) {
2371 continue;
2372 }
Fred Quintana918339a2010-10-05 14:00:39 -07002373 runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
2374 activeSyncContext);
2375 }
2376 }
2377 }
2378
2379 private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
2380 ActiveSyncContext activeSyncContext) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002381 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Alon Alberteca75112010-12-08 15:02:33 -08002382
2383 if (activeSyncContext.mIsLinkedToDeath) {
2384 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
2385 activeSyncContext.mIsLinkedToDeath = false;
2386 }
Fred Quintana918339a2010-10-05 14:00:39 -07002387 closeActiveSyncContext(activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002388
2389 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
2390
2391 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
2392
2393 String historyMessage;
2394 int downstreamActivity;
2395 int upstreamActivity;
2396 if (syncResult != null) {
2397 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002398 Log.v(TAG, "runSyncFinishedOrCanceled [finished]: "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002399 + syncOperation + ", result " + syncResult);
2400 }
2401
2402 if (!syncResult.hasError()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002403 historyMessage = SyncStorageEngine.MESG_SUCCESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002404 // TODO: set these correctly when the SyncResult is extended to include it
2405 downstreamActivity = 0;
2406 upstreamActivity = 0;
Alon Albert6e079a32010-11-12 12:41:09 -08002407 clearBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -08002409 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
2410 // the operation failed so increase the backoff time
2411 if (!syncResult.syncAlreadyInProgress) {
2412 increaseBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002413 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002414 // reschedule the sync if so indicated by the syncResult
2415 maybeRescheduleSync(syncResult, syncOperation);
Alon Albert57286f92012-10-09 14:21:38 -07002416 historyMessage = ContentResolver.syncErrorToString(
2417 syncResultToErrorNumber(syncResult));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002418 // TODO: set these correctly when the SyncResult is extended to include it
2419 downstreamActivity = 0;
2420 upstreamActivity = 0;
2421 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002422
2423 setDelayUntilTime(syncOperation, syncResult.delayUntil);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002424 } else {
2425 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002426 Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002427 }
Fred Quintana718d8a22009-04-29 17:53:20 -07002428 if (activeSyncContext.mSyncAdapter != null) {
2429 try {
Fred Quintana21bb0de2009-06-16 10:24:58 -07002430 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002431 } catch (RemoteException e) {
2432 // we don't need to retry this in this case
2433 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002434 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002435 historyMessage = SyncStorageEngine.MESG_CANCELED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002436 downstreamActivity = 0;
2437 upstreamActivity = 0;
2438 }
2439
2440 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
2441 upstreamActivity, downstreamActivity, elapsedTime);
2442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002443 if (syncResult != null && syncResult.tooManyDeletions) {
2444 installHandleTooManyDeletesNotification(syncOperation.account,
Dianne Hackborn41203752012-08-31 14:05:51 -07002445 syncOperation.authority, syncResult.stats.numDeletes,
2446 syncOperation.userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002447 } else {
Dianne Hackborn41203752012-08-31 14:05:51 -07002448 mNotificationMgr.cancelAsUser(null,
2449 syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(),
2450 new UserHandle(syncOperation.userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002451 }
2452
2453 if (syncResult != null && syncResult.fullSyncRequested) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002454 scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId,
Alon Albert57286f92012-10-09 14:21:38 -07002455 syncOperation.reason,
Fred Quintana918339a2010-10-05 14:00:39 -07002456 syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002457 syncOperation.backoff, syncOperation.delayUntil,
2458 syncOperation.allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 }
2460 // no need to schedule an alarm, as that will be done by our caller.
2461 }
2462
Fred Quintana918339a2010-10-05 14:00:39 -07002463 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
2464 activeSyncContext.close();
2465 mActiveSyncContexts.remove(activeSyncContext);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002466 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
2467 activeSyncContext.mSyncOperation.userId);
Fred Quintana918339a2010-10-05 14:00:39 -07002468 }
2469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002470 /**
2471 * Convert the error-containing SyncResult into the Sync.History error number. Since
2472 * the SyncResult may indicate multiple errors at once, this method just returns the
2473 * most "serious" error.
2474 * @param syncResult the SyncResult from which to read
2475 * @return the most "serious" error set in the SyncResult
2476 * @throws IllegalStateException if the SyncResult does not indicate any errors.
2477 * If SyncResult.error() is true then it is safe to call this.
2478 */
2479 private int syncResultToErrorNumber(SyncResult syncResult) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002480 if (syncResult.syncAlreadyInProgress)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002481 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002482 if (syncResult.stats.numAuthExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002483 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002484 if (syncResult.stats.numIoExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002485 return ContentResolver.SYNC_ERROR_IO;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002486 if (syncResult.stats.numParseExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002487 return ContentResolver.SYNC_ERROR_PARSE;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002488 if (syncResult.stats.numConflictDetectedExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002489 return ContentResolver.SYNC_ERROR_CONFLICT;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002490 if (syncResult.tooManyDeletions)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002491 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002492 if (syncResult.tooManyRetries)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002493 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002494 if (syncResult.databaseError)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002495 return ContentResolver.SYNC_ERROR_INTERNAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002496 throw new IllegalStateException("we are not in an error state, " + syncResult);
2497 }
2498
Fred Quintana918339a2010-10-05 14:00:39 -07002499 private void manageSyncNotificationLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002500 boolean shouldCancel;
2501 boolean shouldInstall;
2502
Fred Quintana918339a2010-10-05 14:00:39 -07002503 if (mActiveSyncContexts.isEmpty()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 mSyncNotificationInfo.startTime = null;
2505
2506 // we aren't syncing. if the notification is active then remember that we need
2507 // to cancel it and then clear out the info
2508 shouldCancel = mSyncNotificationInfo.isActive;
2509 shouldInstall = false;
2510 } else {
2511 // we are syncing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002512 final long now = SystemClock.elapsedRealtime();
2513 if (mSyncNotificationInfo.startTime == null) {
2514 mSyncNotificationInfo.startTime = now;
2515 }
2516
Fred Quintana918339a2010-10-05 14:00:39 -07002517 // there are three cases:
2518 // - the notification is up: do nothing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002519 // - the notification is not up but it isn't time yet: don't install
2520 // - the notification is not up and it is time: need to install
2521
2522 if (mSyncNotificationInfo.isActive) {
Fred Quintana918339a2010-10-05 14:00:39 -07002523 shouldInstall = shouldCancel = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002524 } else {
Fred Quintana918339a2010-10-05 14:00:39 -07002525 // it isn't currently up, so there is nothing to cancel
2526 shouldCancel = false;
2527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002528 final boolean timeToShowNotification =
2529 now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
Fred Quintana918339a2010-10-05 14:00:39 -07002530 if (timeToShowNotification) {
2531 shouldInstall = true;
2532 } else {
2533 // show the notification immediately if this is a manual sync
2534 shouldInstall = false;
2535 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2536 final boolean manualSync = activeSyncContext.mSyncOperation.extras
2537 .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
2538 if (manualSync) {
2539 shouldInstall = true;
2540 break;
2541 }
2542 }
2543 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002544 }
2545 }
2546
2547 if (shouldCancel && !shouldInstall) {
2548 mNeedSyncActiveNotification = false;
2549 sendSyncStateIntent();
2550 mSyncNotificationInfo.isActive = false;
2551 }
2552
2553 if (shouldInstall) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 mNeedSyncActiveNotification = true;
2555 sendSyncStateIntent();
2556 mSyncNotificationInfo.isActive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002557 }
2558 }
2559
Fred Quintana918339a2010-10-05 14:00:39 -07002560 private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
2561 long nextPendingEventElapsedTime) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002562 // in each of these cases the sync loop will be kicked, which will cause this
2563 // method to be called again
2564 if (!mDataConnectionIsConnected) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002565 if (mStorageIsLow) return;
2566
Fred Quintana918339a2010-10-05 14:00:39 -07002567 // When the status bar notification should be raised
2568 final long notificationTime =
2569 (!mSyncHandler.mSyncNotificationInfo.isActive
2570 && mSyncHandler.mSyncNotificationInfo.startTime != null)
2571 ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY
2572 : Long.MAX_VALUE;
Fred Quintana307da1a2010-01-21 14:24:20 -08002573
Fred Quintana918339a2010-10-05 14:00:39 -07002574 // When we should consider canceling an active sync
2575 long earliestTimeoutTime = Long.MAX_VALUE;
2576 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
2577 final long currentSyncTimeoutTime =
2578 currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
2579 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2580 Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
2581 + currentSyncTimeoutTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002582 }
Fred Quintana918339a2010-10-05 14:00:39 -07002583 if (earliestTimeoutTime > currentSyncTimeoutTime) {
2584 earliestTimeoutTime = currentSyncTimeoutTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002585 }
2586 }
2587
Fred Quintana918339a2010-10-05 14:00:39 -07002588 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2589 Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime);
2590 }
2591
2592 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2593 Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
2594 }
2595
2596 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2597 Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
2598 + nextPeriodicEventElapsedTime);
2599 }
2600 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2601 Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
2602 + nextPendingEventElapsedTime);
2603 }
2604
2605 long alarmTime = Math.min(notificationTime, earliestTimeoutTime);
2606 alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime);
2607 alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
2608
2609 // Bound the alarm time.
2610 final long now = SystemClock.elapsedRealtime();
2611 if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
2612 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2613 Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
2614 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002615 }
Fred Quintana918339a2010-10-05 14:00:39 -07002616 alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
2617 } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) {
2618 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2619 Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, "
2620 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
2621 }
2622 alarmTime = now + SYNC_ALARM_TIMEOUT_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002623 }
2624
2625 // determine if we need to set or cancel the alarm
2626 boolean shouldSet = false;
2627 boolean shouldCancel = false;
2628 final boolean alarmIsActive = mAlarmScheduleTime != null;
Fred Quintana918339a2010-10-05 14:00:39 -07002629 final boolean needAlarm = alarmTime != Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630 if (needAlarm) {
2631 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) {
2632 shouldSet = true;
2633 }
2634 } else {
2635 shouldCancel = alarmIsActive;
2636 }
2637
2638 // set or cancel the alarm as directed
2639 ensureAlarmService();
2640 if (shouldSet) {
Fred Quintana918339a2010-10-05 14:00:39 -07002641 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2642 Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
2643 + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
2644 + " secs from now");
2645 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 mAlarmScheduleTime = alarmTime;
2647 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
2648 mSyncAlarmIntent);
2649 } else if (shouldCancel) {
2650 mAlarmScheduleTime = null;
2651 mAlarmService.cancel(mSyncAlarmIntent);
2652 }
2653 }
2654
2655 private void sendSyncStateIntent() {
2656 Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07002657 syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002658 syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
Fred Quintana918339a2010-10-05 14:00:39 -07002659 syncStateIntent.putExtra("failing", false);
Amith Yamasanicd757062012-10-19 18:23:52 -07002660 mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002661 }
2662
Fred Quintanad9d2f112009-04-23 13:36:27 -07002663 private void installHandleTooManyDeletesNotification(Account account, String authority,
Dianne Hackborn41203752012-08-31 14:05:51 -07002664 long numDeletes, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002665 if (mNotificationMgr == null) return;
Fred Quintanac848b702009-08-25 20:18:46 -07002666
2667 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
2668 authority, 0 /* flags */);
2669 if (providerInfo == null) {
2670 return;
2671 }
2672 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
2673
Fred Quintanab19e62a2010-12-16 13:54:43 -08002674 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002675 clickIntent.putExtra("account", account);
Tadashi G. Takaoka86135d32009-09-24 19:31:44 -07002676 clickIntent.putExtra("authority", authority);
Fred Quintanac848b702009-08-25 20:18:46 -07002677 clickIntent.putExtra("provider", authorityName.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002678 clickIntent.putExtra("numDeletes", numDeletes);
2679
2680 if (!isActivityAvailable(clickIntent)) {
2681 Log.w(TAG, "No activity found to handle too many deletes.");
2682 return;
2683 }
2684
2685 final PendingIntent pendingIntent = PendingIntent
Dianne Hackborn41203752012-08-31 14:05:51 -07002686 .getActivityAsUser(mContext, 0, clickIntent,
2687 PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002688
2689 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
2690 R.string.contentServiceTooManyDeletesNotificationDesc);
2691
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002692 Notification notification =
2693 new Notification(R.drawable.stat_notify_sync_error,
2694 mContext.getString(R.string.contentServiceSync),
2695 System.currentTimeMillis());
2696 notification.setLatestEventInfo(mContext,
2697 mContext.getString(R.string.contentServiceSyncNotificationTitle),
Fred Quintanac848b702009-08-25 20:18:46 -07002698 String.format(tooManyDeletesDescFormat.toString(), authorityName),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002699 pendingIntent);
2700 notification.flags |= Notification.FLAG_ONGOING_EVENT;
Dianne Hackborn41203752012-08-31 14:05:51 -07002701 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(),
2702 notification, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 }
2704
2705 /**
2706 * Checks whether an activity exists on the system image for the given intent.
2707 *
2708 * @param intent The intent for an activity.
2709 * @return Whether or not an activity exists.
2710 */
2711 private boolean isActivityAvailable(Intent intent) {
2712 PackageManager pm = mContext.getPackageManager();
2713 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
2714 int listSize = list.size();
2715 for (int i = 0; i < listSize; i++) {
2716 ResolveInfo resolveInfo = list.get(i);
2717 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
2718 != 0) {
2719 return true;
2720 }
2721 }
2722
2723 return false;
2724 }
2725
2726 public long insertStartSyncEvent(SyncOperation syncOperation) {
2727 final int source = syncOperation.syncSource;
2728 final long now = System.currentTimeMillis();
2729
Dianne Hackborn231cc602009-04-27 17:10:36 -07002730 EventLog.writeEvent(2720, syncOperation.authority,
Doug Zongker44f57472009-09-20 15:52:43 -07002731 SyncStorageEngine.EVENT_START, source,
2732 syncOperation.account.name.hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002733
2734 return mSyncStorageEngine.insertStartSyncEvent(
Alon Albert57286f92012-10-09 14:21:38 -07002735 syncOperation.account, syncOperation.userId, syncOperation.reason,
2736 syncOperation.authority,
2737 now, source, syncOperation.isInitialization(), syncOperation.extras
2738 );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002739 }
2740
2741 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
2742 int upstreamActivity, int downstreamActivity, long elapsedTime) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002743 EventLog.writeEvent(2720, syncOperation.authority,
Doug Zongker44f57472009-09-20 15:52:43 -07002744 SyncStorageEngine.EVENT_STOP, syncOperation.syncSource,
2745 syncOperation.account.name.hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002746
Fred Quintana77c560f2010-03-29 22:20:26 -07002747 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002748 resultMessage, downstreamActivity, upstreamActivity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 }
2750 }
Fred Quintana918339a2010-10-05 14:00:39 -07002751
2752 private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
2753 for (ActiveSyncContext sync : mActiveSyncContexts) {
2754 if (sync == activeSyncContext) {
2755 return true;
2756 }
2757 }
2758 return false;
2759 }
Alon Albert57286f92012-10-09 14:21:38 -07002760
2761 static class PrintTable {
2762 private ArrayList<Object[]> mTable = Lists.newArrayList();
2763 private final int mCols;
2764
2765 PrintTable(int cols) {
2766 mCols = cols;
2767 }
2768
2769 void set(int row, int col, Object... values) {
2770 if (col + values.length > mCols) {
2771 throw new IndexOutOfBoundsException("Table only has " + mCols +
2772 " columns. can't set " + values.length + " at column " + col);
2773 }
2774 for (int i = mTable.size(); i <= row; i++) {
2775 final Object[] list = new Object[mCols];
2776 mTable.add(list);
2777 for (int j = 0; j < mCols; j++) {
2778 list[j] = "";
2779 }
2780 }
2781 System.arraycopy(values, 0, mTable.get(row), col, values.length);
2782 }
2783
2784 void writeTo(PrintWriter out) {
2785 final String[] formats = new String[mCols];
2786 int totalLength = 0;
2787 for (int col = 0; col < mCols; ++col) {
2788 int maxLength = 0;
2789 for (Object[] row : mTable) {
2790 final int length = row[col].toString().length();
2791 if (length > maxLength) {
2792 maxLength = length;
2793 }
2794 }
2795 totalLength += maxLength;
2796 formats[col] = String.format("%%-%ds", maxLength);
2797 }
2798 printRow(out, formats, mTable.get(0));
2799 totalLength += (mCols - 1) * 2;
2800 for (int i = 0; i < totalLength; ++i) {
2801 out.print("-");
2802 }
2803 out.println();
2804 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
2805 Object[] row = mTable.get(i);
2806 printRow(out, formats, row);
2807 }
2808 }
2809
2810 private void printRow(PrintWriter out, String[] formats, Object[] row) {
2811 for (int j = 0, rowLength = row.length; j < rowLength; j++) {
2812 out.printf(String.format(formats[j], row[j].toString()));
2813 out.print(" ");
2814 }
2815 out.println();
2816 }
2817
2818 public int getNumRows() {
2819 return mTable.size();
2820 }
2821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002822}