blob: 71d8d9911f9ac0d063631e1721881a0062955f63 [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;
Matthew Williamsfa774182013-06-18 15:44:11 -070037import android.content.PeriodicSync;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080038import android.content.ServiceConnection;
39import android.content.SyncActivityTooManyDeletes;
40import android.content.SyncAdapterType;
41import android.content.SyncAdaptersCache;
42import android.content.SyncInfo;
43import android.content.SyncResult;
44import android.content.SyncStatusInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070045import android.content.pm.ApplicationInfo;
Amith Yamasani9422bdc2013-04-10 16:58:19 -070046import android.content.pm.PackageInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070047import android.content.pm.PackageManager;
48import android.content.pm.ProviderInfo;
49import android.content.pm.RegisteredServicesCache;
50import android.content.pm.RegisteredServicesCacheListener;
51import android.content.pm.ResolveInfo;
Amith Yamasani04e0d262012-02-14 11:50:53 -080052import android.content.pm.UserInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070053import android.net.ConnectivityManager;
54import android.net.NetworkInfo;
Fred Quintana918339a2010-10-05 14:00:39 -070055import android.os.Bundle;
56import android.os.Handler;
Fred Quintana918339a2010-10-05 14:00:39 -070057import android.os.IBinder;
58import android.os.Looper;
59import android.os.Message;
60import android.os.PowerManager;
Fred Quintana918339a2010-10-05 14:00:39 -070061import android.os.RemoteException;
62import android.os.SystemClock;
63import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070064import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070065import android.os.UserManager;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070066import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.text.format.DateUtils;
69import android.text.format.Time;
Matthew Williams632515b2013-10-10 15:51:00 -070070import android.text.TextUtils;
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;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070077import com.android.internal.os.BackgroundThread;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070078import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080079import com.android.server.accounts.AccountManagerService;
Georgi Nikolovdbe846b2013-06-25 14:09:56 -070080import com.android.server.content.SyncStorageEngine.AuthorityInfo;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080081import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
Alon Albert8e285552012-09-17 15:05:27 -070082import com.google.android.collect.Lists;
83import com.google.android.collect.Maps;
84import com.google.android.collect.Sets;
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import java.io.PrintWriter;
Matthew Williamsfa774182013-06-18 15:44:11 -070088import java.text.SimpleDateFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import java.util.ArrayList;
Alon Alberte0bde332011-09-22 14:26:16 -070090import java.util.Arrays;
Matthew Williams62222882013-08-20 15:32:20 -070091import java.util.Arrays;
Fred Quintana918339a2010-10-05 14:00:39 -070092import java.util.Collection;
93import java.util.Collections;
Alon Alberte0bde332011-09-22 14:26:16 -070094import java.util.Comparator;
Fred Quintanab3029c32010-04-06 13:27:12 -070095import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070096import java.util.HashSet;
Fred Quintana918339a2010-10-05 14:00:39 -070097import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098import java.util.List;
Alon Alberte0bde332011-09-22 14:26:16 -070099import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100import java.util.Random;
Alon Albert8e285552012-09-17 15:05:27 -0700101import java.util.Set;
Fred Quintanae91ebe22009-09-29 20:44:30 -0700102import java.util.concurrent.CountDownLatch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
104/**
105 * @hide
106 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700107public class SyncManager {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 private static final String TAG = "SyncManager";
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 /** Delay a sync due to local changes this long. In milliseconds */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700111 private static final long LOCAL_SYNC_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
113 /**
114 * If a sync takes longer than this and the sync queue is not empty then we will
115 * cancel it and add it back to the end of the sync queue. In milliseconds.
116 */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700117 private static final long MAX_TIME_PER_SYNC;
118
119 static {
Dianne Hackbornb4e12492013-06-26 15:08:19 -0700120 final boolean isLargeRAM = !ActivityManager.isLowRamDeviceStatic();
Fred Quintana33e44692011-12-05 15:04:16 -0800121 int defaultMaxInitSyncs = isLargeRAM ? 5 : 2;
122 int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1;
123 MAX_SIMULTANEOUS_INITIALIZATION_SYNCS =
124 SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs);
125 MAX_SIMULTANEOUS_REGULAR_SYNCS =
126 SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs);
Fred Quintana918339a2010-10-05 14:00:39 -0700127 LOCAL_SYNC_DELAY =
128 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
129 MAX_TIME_PER_SYNC =
130 SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
131 SYNC_NOTIFICATION_DELAY =
132 SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
Fred Quintana918339a2010-10-05 14:00:39 -0700135 private static final long SYNC_NOTIFICATION_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
137 /**
138 * When retrying a sync for the first time use this delay. After that
139 * the retry time will double until it reached MAX_SYNC_RETRY_TIME.
140 * In milliseconds.
141 */
142 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds
143
144 /**
145 * Default the max sync retry time to this value.
146 */
147 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour
148
149 /**
Fred Quintana8570f742010-02-18 10:32:54 -0800150 * How long to wait before retrying a sync that failed due to one already being in progress.
151 */
152 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
153
Fred Quintana3ec47302010-03-10 10:08:31 -0800154 private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
155
Dianne Hackborne746f032010-09-13 16:02:57 -0700156 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700157 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
Fred Quintana918339a2010-10-05 14:00:39 -0700158 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
159
160 private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
161 private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
163 private Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
Amith Yamasani04e0d262012-02-14 11:50:53 -0800165 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
166
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700167 // TODO: add better locking around mRunningAccounts
168 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
Fred Quintana918339a2010-10-05 14:00:39 -0700171 volatile private PowerManager.WakeLock mSyncManagerWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 volatile private boolean mDataConnectionIsConnected = false;
173 volatile private boolean mStorageIsLow = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174
175 private final NotificationManager mNotificationMgr;
176 private AlarmManager mAlarmService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700178 private SyncStorageEngine mSyncStorageEngine;
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700179
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800180 @GuardedBy("mSyncQueue")
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700181 private final SyncQueue mSyncQueue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700183 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 // set if the sync active indicator should be reported
186 private boolean mNeedSyncActiveNotification = false;
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 private final PendingIntent mSyncAlarmIntent;
Fred Quintanaf892fb32009-08-27 21:32:08 -0700189 // Synchronized on "this". Instead of using this directly one should instead call
190 // its accessor, getConnManager().
191 private ConnectivityManager mConnManagerDoNotUseDirectly;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700193 protected SyncAdaptersCache mSyncAdapters;
Fred Quintana718d8a22009-04-29 17:53:20 -0700194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 private BroadcastReceiver mStorageIntentReceiver =
196 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700197 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 String action = intent.getAction();
200 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
201 if (Log.isLoggable(TAG, Log.VERBOSE)) {
202 Log.v(TAG, "Internal storage is low.");
203 }
204 mStorageIsLow = true;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700205 cancelActiveSync(null /* any account */, UserHandle.USER_ALL,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800206 null /* any authority */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
208 if (Log.isLoggable(TAG, Log.VERBOSE)) {
209 Log.v(TAG, "Internal storage is ok.");
210 }
211 mStorageIsLow = false;
212 sendCheckAlarmsMessage();
213 }
214 }
215 };
216
Fred Quintana60307342009-03-24 22:48:12 -0700217 private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700218 @Override
Fred Quintana60307342009-03-24 22:48:12 -0700219 public void onReceive(Context context, Intent intent) {
Fred Quintanae91ebe22009-09-29 20:44:30 -0700220 mSyncHandler.onBootCompleted();
Fred Quintana60307342009-03-24 22:48:12 -0700221 }
222 };
223
Fred Quintana600dde02009-09-23 12:44:10 -0700224 private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700225 @Override
Fred Quintana600dde02009-09-23 12:44:10 -0700226 public void onReceive(Context context, Intent intent) {
227 if (getConnectivityManager().getBackgroundDataSetting()) {
Alon Albert57286f92012-10-09 14:21:38 -0700228 scheduleSync(null /* account */, UserHandle.USER_ALL,
229 SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED,
230 null /* authority */,
Matthew Williamsfa774182013-06-18 15:44:11 -0700231 new Bundle(), 0 /* delay */, 0 /* delay */,
Fred Quintana600dde02009-09-23 12:44:10 -0700232 false /* onlyThoseWithUnknownSyncableState */);
233 }
234 }
235 };
236
Amith Yamasanid648a602012-09-26 15:06:10 -0700237 private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700238 @Override
Amith Yamasanid648a602012-09-26 15:06:10 -0700239 public void onReceive(Context context, Intent intent) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700240 updateRunningAccounts();
241
242 // Kick off sync for everyone, since this was a radical account change
Alon Albert57286f92012-10-09 14:21:38 -0700243 scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null,
Matthew Williamsfa774182013-06-18 15:44:11 -0700244 null, 0 /* no delay */, 0/* no delay */, false);
Amith Yamasanid648a602012-09-26 15:06:10 -0700245 }
246 };
247
Fred Quintanab3029c32010-04-06 13:27:12 -0700248 private final PowerManager mPowerManager;
249
Matthew Williamsfa774182013-06-18 15:44:11 -0700250 // Use this as a random offset to seed all periodic syncs.
Ashish Sharma69d95de2012-04-11 17:27:24 -0700251 private int mSyncRandomOffsetMillis;
252
Amith Yamasani9535c912012-10-10 21:48:33 -0700253 private final UserManager mUserManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700254
Fred Quintana918339a2010-10-05 14:00:39 -0700255 private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
256 private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
257
Amith Yamasani258848d2012-08-10 17:06:33 -0700258 private List<UserInfo> getAllUsers() {
Amith Yamasani9535c912012-10-10 21:48:33 -0700259 return mUserManager.getUsers();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800260 }
261
262 private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
263 boolean found = false;
264 for (int i = 0; i < accounts.length; i++) {
265 if (accounts[i].userId == userId
266 && accounts[i].account.equals(account)) {
267 found = true;
268 break;
269 }
270 }
271 return found;
272 }
273
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700274 public void updateRunningAccounts() {
275 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800276
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700277 if (mBootCompleted) {
278 doDatabaseCleanup();
279 }
280
Fred Quintana918339a2010-10-05 14:00:39 -0700281 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700282 if (!containsAccountAndUser(mRunningAccounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800283 currentSyncContext.mSyncOperation.account,
284 currentSyncContext.mSyncOperation.userId)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700285 Log.d(TAG, "canceling sync since the account is no longer running");
Fred Quintana918339a2010-10-05 14:00:39 -0700286 sendSyncFinishedOrCanceledMessage(currentSyncContext,
Fred Quintanad9d2f112009-04-23 13:36:27 -0700287 null /* no result since this is a cancel */);
288 }
289 }
290
291 // we must do this since we don't bother scheduling alarms when
292 // the accounts are not set yet
293 sendCheckAlarmsMessage();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700294 }
295
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700296 private void doDatabaseCleanup() {
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700297 for (UserInfo user : mUserManager.getUsers(true)) {
298 // Skip any partially created/removed users
299 if (user.partial) continue;
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700300 Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
301 mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
302 }
303 }
304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 private BroadcastReceiver mConnectivityIntentReceiver =
306 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700307 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 public void onReceive(Context context, Intent intent) {
Alon Alberted1d2532011-02-15 14:02:14 -0800309 final boolean wasConnected = mDataConnectionIsConnected;
310
311 // don't use the intent to figure out if network is connected, just check
312 // ConnectivityManager directly.
Alon Albert1bad83a2011-02-16 10:29:56 -0800313 mDataConnectionIsConnected = readDataConnectionState();
Alon Alberted1d2532011-02-15 14:02:14 -0800314 if (mDataConnectionIsConnected) {
315 if (!wasConnected) {
316 if (Log.isLoggable(TAG, Log.VERBOSE)) {
317 Log.v(TAG, "Reconnection detected: clearing all backoffs");
318 }
Matthew Williamsed37b932013-09-12 14:30:09 -0700319 synchronized(mSyncQueue) {
320 mSyncStorageEngine.clearAllBackoffsLocked(mSyncQueue);
321 }
Alon Alberted1d2532011-02-15 14:02:14 -0800322 }
323 sendCheckAlarmsMessage();
324 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
326 };
327
Alon Albert1bad83a2011-02-16 10:29:56 -0800328 private boolean readDataConnectionState() {
Alon Alberted1d2532011-02-15 14:02:14 -0800329 NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
330 return (networkInfo != null) && networkInfo.isConnected();
331 }
332
Dianne Hackborn55280a92009-05-07 15:53:46 -0700333 private BroadcastReceiver mShutdownIntentReceiver =
334 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700335 @Override
Dianne Hackborn55280a92009-05-07 15:53:46 -0700336 public void onReceive(Context context, Intent intent) {
337 Log.w(TAG, "Writing sync state before shutdown...");
338 getSyncStorageEngine().writeAllState();
339 }
340 };
341
Amith Yamasani13593602012-03-22 16:16:17 -0700342 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
343 @Override
344 public void onReceive(Context context, Intent intent) {
Alon Albert8e285552012-09-17 15:05:27 -0700345 String action = intent.getAction();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700346 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
347 if (userId == UserHandle.USER_NULL) return;
348
Alon Albert8e285552012-09-17 15:05:27 -0700349 if (Intent.ACTION_USER_REMOVED.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700350 onUserRemoved(userId);
351 } else if (Intent.ACTION_USER_STARTING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700352 onUserStarting(userId);
353 } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700354 onUserStopping(userId);
Alon Albert8e285552012-09-17 15:05:27 -0700355 }
Amith Yamasani13593602012-03-22 16:16:17 -0700356 }
357 };
358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 private final SyncHandler mSyncHandler;
361
Fred Quintana4f9cfc52009-09-02 15:20:23 -0700362 private volatile boolean mBootCompleted = false;
363
Fred Quintanaf892fb32009-08-27 21:32:08 -0700364 private ConnectivityManager getConnectivityManager() {
365 synchronized (this) {
366 if (mConnManagerDoNotUseDirectly == null) {
367 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
368 Context.CONNECTIVITY_SERVICE);
369 }
370 return mConnManagerDoNotUseDirectly;
371 }
372 }
373
Jeff Sharkeye4996bb2012-10-17 14:16:28 -0700374 /**
375 * Should only be created after {@link ContentService#systemReady()} so that
376 * {@link PackageManager} is ready to query.
377 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 public SyncManager(Context context, boolean factoryTest) {
379 // Initialize the SyncStorageEngine first, before registering observers
380 // and creating threads and so on; it may fail if the disk is full.
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700381 mContext = context;
Amith Yamasani9535c912012-10-10 21:48:33 -0700382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 SyncStorageEngine.init(context);
384 mSyncStorageEngine = SyncStorageEngine.getSingleton();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800385 mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700386 @Override
Alon Albert57286f92012-10-09 14:21:38 -0700387 public void onSyncRequest(Account account, int userId, int reason, String authority,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800388 Bundle extras) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700389 scheduleSync(account, userId, reason, authority, extras,
390 0 /* no delay */,
391 0 /* no delay */,
392 false);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800393 }
394 });
395
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700396 mSyncAdapters = new SyncAdaptersCache(mContext);
Alon Albert57286f92012-10-09 14:21:38 -0700397 mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700399 mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
Fred Quintana44037e62010-01-21 13:14:49 -0800401 mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700402 @Override
403 public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
Fred Quintana44037e62010-01-21 13:14:49 -0800404 if (!removed) {
Alon Albert57286f92012-10-09 14:21:38 -0700405 scheduleSync(null, UserHandle.USER_ALL,
406 SyncOperation.REASON_SERVICE_CHANGED,
Matthew Williamsfa774182013-06-18 15:44:11 -0700407 type.authority, null, 0 /* no delay */, 0 /* no delay */,
Fred Quintana44037e62010-01-21 13:14:49 -0800408 false /* onlyThoseWithUnkownSyncableState */);
409 }
410 }
411 }, mSyncHandler);
Fred Quintana718d8a22009-04-29 17:53:20 -0700412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 mSyncAlarmIntent = PendingIntent.getBroadcast(
414 mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
417 context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
418
Fred Quintanae91ebe22009-09-29 20:44:30 -0700419 if (!factoryTest) {
420 intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
421 context.registerReceiver(mBootCompletedReceiver, intentFilter);
422 }
Fred Quintana60307342009-03-24 22:48:12 -0700423
Fred Quintana600dde02009-09-23 12:44:10 -0700424 intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
425 context.registerReceiver(mBackgroundDataSettingChanged, intentFilter);
426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
428 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
429 context.registerReceiver(mStorageIntentReceiver, intentFilter);
430
Dianne Hackborn55280a92009-05-07 15:53:46 -0700431 intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
432 intentFilter.setPriority(100);
433 context.registerReceiver(mShutdownIntentReceiver, intentFilter);
434
Amith Yamasani13593602012-03-22 16:16:17 -0700435 intentFilter = new IntentFilter();
436 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700437 intentFilter.addAction(Intent.ACTION_USER_STARTING);
438 intentFilter.addAction(Intent.ACTION_USER_STOPPING);
Alon Albert8e285552012-09-17 15:05:27 -0700439 mContext.registerReceiverAsUser(
440 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
Amith Yamasani13593602012-03-22 16:16:17 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 if (!factoryTest) {
443 mNotificationMgr = (NotificationManager)
444 context.getSystemService(Context.NOTIFICATION_SERVICE);
445 context.registerReceiver(new SyncAlarmIntentReceiver(),
446 new IntentFilter(ACTION_SYNC_ALARM));
447 } else {
448 mNotificationMgr = null;
449 }
Fred Quintanab3029c32010-04-06 13:27:12 -0700450 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
Amith Yamasani9535c912012-10-10 21:48:33 -0700451 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452
453 // This WakeLock is used to ensure that we stay awake between the time that we receive
454 // a sync alarm notification and when we finish processing it. We need to do this
455 // because we don't do the work in the alarm handler, rather we do it in a message
456 // handler.
Fred Quintanab3029c32010-04-06 13:27:12 -0700457 mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 HANDLE_SYNC_ALARM_WAKE_LOCK);
459 mHandleAlarmWakeLock.setReferenceCounted(false);
460
Fred Quintana918339a2010-10-05 14:00:39 -0700461 // This WakeLock is used to ensure that we stay awake while running the sync loop
462 // message handler. Normally we will hold a sync adapter wake lock while it is being
463 // synced but during the execution of the sync loop it might finish a sync for
464 // one sync adapter before starting the sync for the other sync adapter and we
465 // don't want the device to go to sleep during that window.
466 mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
467 SYNC_LOOP_WAKE_LOCK);
468 mSyncManagerWakeLock.setReferenceCounted(false);
469
Dianne Hackborn231cc602009-04-27 17:10:36 -0700470 mSyncStorageEngine.addStatusChangeListener(
Fred Quintanaac9385e2009-06-22 18:00:59 -0700471 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700472 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700473 public void onStatusChanged(int which) {
474 // force the sync loop to run if the settings change
475 sendCheckAlarmsMessage();
476 }
477 });
Fred Quintanae91ebe22009-09-29 20:44:30 -0700478
479 if (!factoryTest) {
Amith Yamasanid648a602012-09-26 15:06:10 -0700480 // Register for account list updates for all users
481 mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
482 UserHandle.ALL,
483 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
484 null, null);
Fred Quintanae91ebe22009-09-29 20:44:30 -0700485 }
Ashish Sharma69d95de2012-04-11 17:27:24 -0700486
487 // Pick a random second in a day to seed all periodic syncs
488 mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 }
490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 /**
492 * Return a random value v that satisfies minValue <= v < maxValue. The difference between
493 * maxValue and minValue must be less than Integer.MAX_VALUE.
494 */
495 private long jitterize(long minValue, long maxValue) {
496 Random random = new Random(SystemClock.elapsedRealtime());
497 long spread = maxValue - minValue;
498 if (spread > Integer.MAX_VALUE) {
499 throw new IllegalArgumentException("the difference between the maxValue and the "
500 + "minValue must be less than " + Integer.MAX_VALUE);
501 }
502 return minValue + random.nextInt((int)spread);
503 }
504
Dianne Hackborn231cc602009-04-27 17:10:36 -0700505 public SyncStorageEngine getSyncStorageEngine() {
506 return mSyncStorageEngine;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 }
Doug Zongker44f57472009-09-20 15:52:43 -0700508
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700509 public int getIsSyncable(Account account, int userId, String providerName) {
510 int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
511 UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
512
513 // If it's not a restricted user, return isSyncable
514 if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
515
516 // Else check if the sync adapter has opted-in or not
517 RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
518 mSyncAdapters.getServiceInfo(
519 SyncAdapterType.newKey(providerName, account.type), userId);
520 if (syncAdapterInfo == null) return isSyncable;
521
522 PackageInfo pInfo = null;
523 try {
524 pInfo = AppGlobals.getPackageManager().getPackageInfo(
525 syncAdapterInfo.componentName.getPackageName(), 0, userId);
526 if (pInfo == null) return isSyncable;
527 } catch (RemoteException re) {
528 // Shouldn't happen
529 return isSyncable;
530 }
531 if (pInfo.restrictedAccountType != null
532 && pInfo.restrictedAccountType.equals(account.type)) {
533 return isSyncable;
534 } else {
535 return 0;
536 }
537 }
538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 private void ensureAlarmService() {
540 if (mAlarmService == null) {
541 mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
542 }
543 }
544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700546 * Initiate a sync. This can start a sync for all providers
547 * (pass null to url, set onlyTicklable to false), only those
548 * providers that are marked as ticklable (pass null to url,
549 * set onlyTicklable to true), or a specific provider (set url
550 * to the content url of the provider).
551 *
552 * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
553 * true then initiate a sync that just checks for local changes to send
554 * to the server, otherwise initiate a sync that first gets any
555 * changes from the server before sending local changes back to
556 * the server.
557 *
558 * <p>If a specific provider is being synced (the url is non-null)
559 * then the extras can contain SyncAdapter-specific information
560 * to control what gets synced (e.g. which specific feed to sync).
561 *
562 * <p>You'll start getting callbacks after this.
563 *
564 * @param requestedAccount the account to sync, may be null to signify all accounts
565 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
566 * then all users' accounts are considered.
567 * @param reason for sync request. If this is a positive integer, it is the Linux uid
568 * assigned to the process that requested the sync. If it's negative, the sync was requested by
569 * the SyncManager itself and could be one of the following:
570 * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
571 * {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
572 * {@link SyncOperation#REASON_SERVICE_CHANGED}
573 * {@link SyncOperation#REASON_PERIODIC}
574 * {@link SyncOperation#REASON_IS_SYNCABLE}
575 * {@link SyncOperation#REASON_SYNC_AUTO}
576 * {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
577 * {@link SyncOperation#REASON_USER_START}
578 * @param requestedAuthority the authority to sync, may be null to indicate all authorities
579 * @param extras a Map of SyncAdapter-specific information to control
580 * syncs of a specific provider. Can be null. Is ignored
581 * if the url is null.
582 * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run.
583 * @param runtimeMillis maximum milliseconds in the future to wait before performing sync.
584 * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
585 */
586 public void scheduleSync(Account requestedAccount, int userId, int reason,
587 String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
588 long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
589 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
590
591 final boolean backgroundDataUsageAllowed = !mBootCompleted ||
592 getConnectivityManager().getBackgroundDataSetting();
593
594 if (extras == null) {
595 extras = new Bundle();
596 }
597 if (isLoggable) {
598 Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " "
599 + requestedAuthority);
600 }
601 Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
602 if (expedited) {
603 runtimeMillis = -1; // this means schedule at the front of the queue
604 }
605
606 AccountAndUser[] accounts;
607 if (requestedAccount != null && userId != UserHandle.USER_ALL) {
608 accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
609 } else {
610 // if the accounts aren't configured yet then we can't support an account-less
611 // sync request
612 accounts = mRunningAccounts;
613 if (accounts.length == 0) {
614 if (isLoggable) {
615 Log.v(TAG, "scheduleSync: no accounts configured, dropping");
616 }
617 return;
618 }
619 }
620
621 final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
622 final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
623 if (manualSync) {
624 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
625 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
626 }
627 final boolean ignoreSettings =
628 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
629
630 int source;
631 if (uploadOnly) {
632 source = SyncStorageEngine.SOURCE_LOCAL;
633 } else if (manualSync) {
634 source = SyncStorageEngine.SOURCE_USER;
635 } else if (requestedAuthority == null) {
636 source = SyncStorageEngine.SOURCE_POLL;
637 } else {
638 // this isn't strictly server, since arbitrary callers can (and do) request
639 // a non-forced two-way sync on a specific url
640 source = SyncStorageEngine.SOURCE_SERVER;
641 }
642
643 for (AccountAndUser account : accounts) {
644 // Compile a list of authorities that have sync adapters.
645 // For each authority sync each account that matches a sync adapter.
646 final HashSet<String> syncableAuthorities = new HashSet<String>();
647 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
648 mSyncAdapters.getAllServices(account.userId)) {
649 syncableAuthorities.add(syncAdapter.type.authority);
650 }
651
652 // if the url was specified then replace the list of authorities
653 // with just this authority or clear it if this authority isn't
654 // syncable
655 if (requestedAuthority != null) {
656 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
657 syncableAuthorities.clear();
658 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
659 }
660
661 for (String authority : syncableAuthorities) {
662 int isSyncable = getIsSyncable(account.account, account.userId,
663 authority);
664 if (isSyncable == 0) {
665 continue;
666 }
667 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
668 syncAdapterInfo = mSyncAdapters.getServiceInfo(
669 SyncAdapterType.newKey(authority, account.account.type), account.userId);
670 if (syncAdapterInfo == null) {
671 continue;
672 }
673 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
674 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
675 if (isSyncable < 0 && isAlwaysSyncable) {
676 mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
677 isSyncable = 1;
678 }
679 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
680 continue;
681 }
682 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
683 continue;
684 }
685
686 // always allow if the isSyncable state is unknown
687 boolean syncAllowed =
688 (isSyncable < 0)
689 || ignoreSettings
690 || (backgroundDataUsageAllowed
691 && mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
692 && mSyncStorageEngine.getSyncAutomatically(account.account,
693 account.userId, authority));
694 if (!syncAllowed) {
695 if (isLoggable) {
696 Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
697 + " is not allowed, dropping request");
698 }
699 continue;
700 }
701
702 Pair<Long, Long> backoff = mSyncStorageEngine
703 .getBackoff(account.account, account.userId, authority);
704 long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account,
705 account.userId, authority);
706 final long backoffTime = backoff != null ? backoff.first : 0;
707 if (isSyncable < 0) {
708 // Initialisation sync.
709 Bundle newExtras = new Bundle();
710 newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
711 if (isLoggable) {
712 Log.v(TAG, "schedule initialisation Sync:"
713 + ", delay until " + delayUntil
714 + ", run by " + 0
715 + ", source " + source
716 + ", account " + account
717 + ", authority " + authority
718 + ", extras " + newExtras);
719 }
720 scheduleSyncOperation(
721 new SyncOperation(account.account, account.userId, reason, source,
722 authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
723 backoffTime, delayUntil, allowParallelSyncs));
724 }
725 if (!onlyThoseWithUnkownSyncableState) {
726 if (isLoggable) {
727 Log.v(TAG, "scheduleSync:"
728 + " delay until " + delayUntil
729 + " run by " + runtimeMillis
730 + " flex " + beforeRuntimeMillis
731 + ", source " + source
732 + ", account " + account
733 + ", authority " + authority
734 + ", extras " + extras);
735 }
736 scheduleSyncOperation(
737 new SyncOperation(account.account, account.userId, reason, source,
738 authority, extras, runtimeMillis, beforeRuntimeMillis,
739 backoffTime, delayUntil, allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 }
741 }
742 }
743 }
744
Matthew Williamsfa774182013-06-18 15:44:11 -0700745 /**
746 * Schedule sync based on local changes to a provider. Occurs within interval
747 * [LOCAL_SYNC_DELAY, 2*LOCAL_SYNC_DELAY].
748 */
Alon Albert57286f92012-10-09 14:21:38 -0700749 public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 final Bundle extras = new Bundle();
751 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
Matthew Williamsfa774182013-06-18 15:44:11 -0700752 scheduleSync(account, userId, reason, authority, extras,
753 LOCAL_SYNC_DELAY /* earliest run time */,
754 2 * LOCAL_SYNC_DELAY /* latest sync time. */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700755 false /* onlyThoseWithUnkownSyncableState */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 }
757
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700758 public SyncAdapterType[] getSyncAdapterTypes(int userId) {
759 final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
760 serviceInfos = mSyncAdapters.getAllServices(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700761 SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
762 int i = 0;
763 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
764 types[i] = serviceInfo.type;
765 ++i;
766 }
767 return types;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 private void sendSyncAlarmMessage() {
771 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM");
772 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM);
773 }
774
775 private void sendCheckAlarmsMessage() {
776 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
Ashish Sharma69d95de2012-04-11 17:27:24 -0700777 mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
779 }
780
781 private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
782 SyncResult syncResult) {
783 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED");
784 Message msg = mSyncHandler.obtainMessage();
785 msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
786 msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult);
787 mSyncHandler.sendMessage(msg);
788 }
789
Amith Yamasani04e0d262012-02-14 11:50:53 -0800790 private void sendCancelSyncsMessage(final Account account, final int userId,
791 final String authority) {
Fred Quintana918339a2010-10-05 14:00:39 -0700792 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
793 Message msg = mSyncHandler.obtainMessage();
794 msg.what = SyncHandler.MESSAGE_CANCEL;
795 msg.obj = Pair.create(account, authority);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800796 msg.arg1 = userId;
Fred Quintana918339a2010-10-05 14:00:39 -0700797 mSyncHandler.sendMessage(msg);
798 }
799
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 class SyncHandlerMessagePayload {
801 public final ActiveSyncContext activeSyncContext;
802 public final SyncResult syncResult;
803
804 SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) {
805 this.activeSyncContext = syncContext;
806 this.syncResult = syncResult;
807 }
808 }
809
810 class SyncAlarmIntentReceiver extends BroadcastReceiver {
Matthew Williamsfa774182013-06-18 15:44:11 -0700811 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 public void onReceive(Context context, Intent intent) {
813 mHandleAlarmWakeLock.acquire();
814 sendSyncAlarmMessage();
815 }
816 }
817
Alon Albert6e079a32010-11-12 12:41:09 -0800818 private void clearBackoffSetting(SyncOperation op) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800819 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
Alon Albert6e079a32010-11-12 12:41:09 -0800820 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
821 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800822 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0);
Alon Albert6e079a32010-11-12 12:41:09 -0800823 }
824 }
825
Fred Quintana307da1a2010-01-21 14:24:20 -0800826 private void increaseBackoffSetting(SyncOperation op) {
Ashish Sharma69d95de2012-04-11 17:27:24 -0700827 // TODO: Use this function to align it to an already scheduled sync
828 // operation in the specified window
Fred Quintana307da1a2010-01-21 14:24:20 -0800829 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
Fred Quintana307da1a2010-01-21 14:24:20 -0800831 final Pair<Long, Long> previousSettings =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800832 mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority);
Alon Albertaeeb6202010-12-09 16:14:02 -0800833 long newDelayInMs = -1;
834 if (previousSettings != null) {
835 // don't increase backoff before current backoff is expired. This will happen for op's
836 // with ignoreBackoff set.
837 if (now < previousSettings.first) {
838 if (Log.isLoggable(TAG, Log.VERBOSE)) {
839 Log.v(TAG, "Still in backoff, do not increase it. "
840 + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
841 }
842 return;
843 }
844 // Subsequent delays are the double of the previous delay
845 newDelayInMs = previousSettings.second * 2;
846 }
847 if (newDelayInMs <= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS
849 newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
850 (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 }
852
853 // Cap the delay
Jeff Sharkey625239a2012-09-26 22:03:49 -0700854 long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(),
855 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
857 if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
858 newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
859 }
860
Alon Albertc1ac7762010-10-28 13:35:55 -0700861 final long backoff = now + newDelayInMs;
862
Amith Yamasani04e0d262012-02-14 11:50:53 -0800863 mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
Alon Albertc1ac7762010-10-28 13:35:55 -0700864 backoff, newDelayInMs);
865
866 op.backoff = backoff;
867 op.updateEffectiveRunTime();
868
Fred Quintana918339a2010-10-05 14:00:39 -0700869 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800870 mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff);
Fred Quintana918339a2010-10-05 14:00:39 -0700871 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800872 }
873
874 private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
875 final long delayUntil = delayUntilSeconds * 1000;
876 final long absoluteNow = System.currentTimeMillis();
877 long newDelayUntilTime;
878 if (delayUntil > absoluteNow) {
879 newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow);
880 } else {
881 newDelayUntilTime = 0;
882 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800883 mSyncStorageEngine
884 .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime);
Fred Quintana918339a2010-10-05 14:00:39 -0700885 synchronized (mSyncQueue) {
886 mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
887 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 }
889
890 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -0700891 * Cancel the active sync if it matches the authority and account.
892 * @param account limit the cancelations to syncs with this account, if non-null
893 * @param authority limit the cancelations to syncs with this authority, if non-null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800895 public void cancelActiveSync(Account account, int userId, String authority) {
896 sendCancelSyncsMessage(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 }
898
899 /**
900 * Create and schedule a SyncOperation.
901 *
902 * @param syncOperation the SyncOperation to schedule
903 */
904 public void scheduleSyncOperation(SyncOperation syncOperation) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800905 boolean queueChanged;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 synchronized (mSyncQueue) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800907 queueChanged = mSyncQueue.add(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 }
909
Fred Quintana307da1a2010-01-21 14:24:20 -0800910 if (queueChanged) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 if (Log.isLoggable(TAG, Log.VERBOSE)) {
912 Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
913 }
914 sendCheckAlarmsMessage();
915 } else {
916 if (Log.isLoggable(TAG, Log.VERBOSE)) {
917 Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
918 + syncOperation);
919 }
920 }
921 }
922
923 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -0700924 * Remove scheduled sync operations.
925 * @param account limit the removals to operations with this account, if non-null
926 * @param authority limit the removals to operations with this authority, if non-null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 */
Amith Yamasani04e0d262012-02-14 11:50:53 -0800928 public void clearScheduledSyncOperations(Account account, int userId, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 synchronized (mSyncQueue) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800930 mSyncQueue.remove(account, userId, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800932 mSyncStorageEngine.setBackoff(account, userId, authority,
Fred Quintana918339a2010-10-05 14:00:39 -0700933 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 }
935
Fred Quintana307da1a2010-01-21 14:24:20 -0800936 void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
938 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800939 Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941
Fred Quintana53bd2522010-02-05 15:28:12 -0800942 operation = new SyncOperation(operation);
943
944 // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
945 // request. Retries of the request will always honor the backoff, so clear the
946 // flag in case we retry this request.
947 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
948 operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
949 }
950
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800951 // If this sync aborted because the internal sync loop retried too many times then
952 // don't reschedule. Otherwise we risk getting into a retry loop.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 // If the operation succeeded to some extent then retry immediately.
954 // If this was a two-way sync then retry soft errors with an exponential backoff.
955 // If this was an upward sync then schedule a two-way sync immediately.
956 // Otherwise do not reschedule.
Fred Quintana53bd2522010-02-05 15:28:12 -0800957 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
958 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
Fred Quintana307da1a2010-01-21 14:24:20 -0800959 + operation);
Fred Quintana918339a2010-10-05 14:00:39 -0700960 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
961 && !syncResult.syncAlreadyInProgress) {
Fred Quintana53bd2522010-02-05 15:28:12 -0800962 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
Fred Quintana307da1a2010-01-21 14:24:20 -0800963 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
964 + "encountered an error: " + operation);
Fred Quintana53bd2522010-02-05 15:28:12 -0800965 scheduleSyncOperation(operation);
Fred Quintana307da1a2010-01-21 14:24:20 -0800966 } else if (syncResult.tooManyRetries) {
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800967 Log.d(TAG, "not retrying sync operation because it retried too many times: "
Fred Quintana307da1a2010-01-21 14:24:20 -0800968 + operation);
Fred Quintanaaa7edda2009-12-03 14:18:58 -0800969 } else if (syncResult.madeSomeProgress()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800971 Log.d(TAG, "retrying sync operation because even though it had an error "
972 + "it achieved some success");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 }
Fred Quintana53bd2522010-02-05 15:28:12 -0800974 scheduleSyncOperation(operation);
Fred Quintana8570f742010-02-18 10:32:54 -0800975 } else if (syncResult.syncAlreadyInProgress) {
976 if (isLoggable) {
977 Log.d(TAG, "retrying sync operation that failed because there was already a "
978 + "sync in progress: " + operation);
979 }
Matthew Williamsfa774182013-06-18 15:44:11 -0700980 scheduleSyncOperation(
981 new SyncOperation(
982 operation.account, operation.userId,
Alon Albert57286f92012-10-09 14:21:38 -0700983 operation.reason,
Amith Yamasani04e0d262012-02-14 11:50:53 -0800984 operation.syncSource,
Fred Quintana8570f742010-02-18 10:32:54 -0800985 operation.authority, operation.extras,
Matthew Williamsfa774182013-06-18 15:44:11 -0700986 DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, operation.flexTime,
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700987 operation.backoff, operation.delayUntil, operation.allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 } else if (syncResult.hasSoftError()) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800989 if (isLoggable) {
990 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
991 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 }
Fred Quintana53bd2522010-02-05 15:28:12 -0800993 scheduleSyncOperation(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -0800995 Log.d(TAG, "not retrying sync operation because the error is a hard error: "
996 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
998 }
999
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001000 private void onUserStarting(int userId) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001001 // Make sure that accounts we're about to use are valid
1002 AccountManagerService.getSingleton().validateAccounts(userId);
1003
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001004 mSyncAdapters.invalidateCache(userId);
1005
1006 updateRunningAccounts();
1007
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07001008 synchronized (mSyncQueue) {
1009 mSyncQueue.addPendingOperations(userId);
1010 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001011
1012 // Schedule sync for any accounts under started user
Jeff Sharkey8f55d112012-10-11 11:00:21 -07001013 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001014 for (Account account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -07001015 scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
Matthew Williamsfa774182013-06-18 15:44:11 -07001016 0 /* no delay */, 0 /* No flex */,
1017 true /* onlyThoseWithUnknownSyncableState */);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001018 }
1019
1020 sendCheckAlarmsMessage();
Alon Albert8e285552012-09-17 15:05:27 -07001021 }
Amith Yamasani13593602012-03-22 16:16:17 -07001022
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001023 private void onUserStopping(int userId) {
1024 updateRunningAccounts();
1025
1026 cancelActiveSync(
1027 null /* any account */,
1028 userId,
1029 null /* any authority */);
1030 }
1031
1032 private void onUserRemoved(int userId) {
1033 updateRunningAccounts();
1034
Amith Yamasani13593602012-03-22 16:16:17 -07001035 // Clean up the storage engine database
1036 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001037 synchronized (mSyncQueue) {
1038 mSyncQueue.removeUser(userId);
1039 }
1040 }
1041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 /**
1043 * @hide
1044 */
Alon Alberteca75112010-12-08 15:02:33 -08001045 class ActiveSyncContext extends ISyncContext.Stub
1046 implements ServiceConnection, IBinder.DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 final SyncOperation mSyncOperation;
1048 final long mHistoryRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001049 ISyncAdapter mSyncAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 final long mStartTime;
1051 long mTimeoutStartTime;
Fred Quintana3ec47302010-03-10 10:08:31 -08001052 boolean mBound;
Fred Quintana918339a2010-10-05 14:00:39 -07001053 final PowerManager.WakeLock mSyncWakeLock;
1054 final int mSyncAdapterUid;
1055 SyncInfo mSyncInfo;
Alon Alberteca75112010-12-08 15:02:33 -08001056 boolean mIsLinkedToDeath = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057
Fred Quintana918339a2010-10-05 14:00:39 -07001058 /**
1059 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
1060 * sync adapter. Since this grabs the wakelock you need to be sure to call
1061 * close() when you are done with this ActiveSyncContext, whether the sync succeeded
1062 * or not.
1063 * @param syncOperation the SyncOperation we are about to sync
1064 * @param historyRowId the row in which to record the history info for this sync
1065 * @param syncAdapterUid the UID of the application that contains the sync adapter
1066 * for this sync. This is used to attribute the wakelock hold to that application.
1067 */
1068 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
1069 int syncAdapterUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 super();
Fred Quintana918339a2010-10-05 14:00:39 -07001071 mSyncAdapterUid = syncAdapterUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 mSyncOperation = syncOperation;
1073 mHistoryRowId = historyRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001074 mSyncAdapter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 mStartTime = SystemClock.elapsedRealtime();
1076 mTimeoutStartTime = mStartTime;
Fred Quintana918339a2010-10-05 14:00:39 -07001077 mSyncWakeLock = mSyncHandler.getSyncWakeLock(
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001078 mSyncOperation.account, mSyncOperation.authority);
Fred Quintana918339a2010-10-05 14:00:39 -07001079 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
1080 mSyncWakeLock.acquire();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 }
1082
1083 public void sendHeartbeat() {
Fred Quintana307da1a2010-01-21 14:24:20 -08001084 // heartbeats are no longer used
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 }
1086
1087 public void onFinished(SyncResult result) {
Fred Quintana918339a2010-10-05 14:00:39 -07001088 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 // include "this" in the message so that the handler can ignore it if this
1090 // ActiveSyncContext is no longer the mActiveSyncContext at message handling
1091 // time
1092 sendSyncFinishedOrCanceledMessage(this, result);
1093 }
1094
1095 public void toString(StringBuilder sb) {
1096 sb.append("startTime ").append(mStartTime)
1097 .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
1098 .append(", mHistoryRowId ").append(mHistoryRowId)
1099 .append(", syncOperation ").append(mSyncOperation);
1100 }
1101
Fred Quintana718d8a22009-04-29 17:53:20 -07001102 public void onServiceConnected(ComponentName name, IBinder service) {
1103 Message msg = mSyncHandler.obtainMessage();
1104 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
1105 msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
1106 mSyncHandler.sendMessage(msg);
1107 }
1108
1109 public void onServiceDisconnected(ComponentName name) {
1110 Message msg = mSyncHandler.obtainMessage();
1111 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
1112 msg.obj = new ServiceConnectionData(this, null);
1113 mSyncHandler.sendMessage(msg);
1114 }
1115
Dianne Hackborn41203752012-08-31 14:05:51 -07001116 boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001117 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1118 Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
1119 }
1120 Intent intent = new Intent();
1121 intent.setAction("android.content.SyncAdapter");
1122 intent.setComponent(info.componentName);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -07001123 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1124 com.android.internal.R.string.sync_binding_label);
Dianne Hackborn41203752012-08-31 14:05:51 -07001125 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1126 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
1127 null, new UserHandle(userId)));
Fred Quintana3ec47302010-03-10 10:08:31 -08001128 mBound = true;
Amith Yamasani27b89e62013-01-16 12:30:11 -08001129 final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Dianne Hackborne02c88a2011-10-28 13:58:15 -07001130 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Amith Yamasani04e0d262012-02-14 11:50:53 -08001131 | Context.BIND_ALLOW_OOM_MANAGEMENT,
Amith Yamasani27b89e62013-01-16 12:30:11 -08001132 new UserHandle(mSyncOperation.userId));
Fred Quintana3ec47302010-03-10 10:08:31 -08001133 if (!bindResult) {
1134 mBound = false;
1135 }
1136 return bindResult;
Fred Quintana718d8a22009-04-29 17:53:20 -07001137 }
1138
Fred Quintana918339a2010-10-05 14:00:39 -07001139 /**
1140 * Performs the required cleanup, which is the releasing of the wakelock and
1141 * unbinding from the sync adapter (if actually bound).
1142 */
Fred Quintana3ec47302010-03-10 10:08:31 -08001143 protected void close() {
Fred Quintana718d8a22009-04-29 17:53:20 -07001144 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1145 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
1146 }
Fred Quintana3ec47302010-03-10 10:08:31 -08001147 if (mBound) {
1148 mBound = false;
1149 mContext.unbindService(this);
1150 }
Fred Quintana918339a2010-10-05 14:00:39 -07001151 mSyncWakeLock.release();
Dianne Hackbornc24ab862011-10-18 15:55:03 -07001152 mSyncWakeLock.setWorkSource(null);
Fred Quintana718d8a22009-04-29 17:53:20 -07001153 }
1154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001155 @Override
1156 public String toString() {
1157 StringBuilder sb = new StringBuilder();
1158 toString(sb);
1159 return sb.toString();
1160 }
Alon Alberteca75112010-12-08 15:02:33 -08001161
1162 @Override
1163 public void binderDied() {
1164 sendSyncFinishedOrCanceledMessage(this, null);
1165 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 }
1167
1168 protected void dump(FileDescriptor fd, PrintWriter pw) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001169 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1170 dumpSyncState(ipw);
1171 dumpSyncHistory(ipw);
1172 dumpSyncAdapters(ipw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 }
1174
Dianne Hackborn231cc602009-04-27 17:10:36 -07001175 static String formatTime(long time) {
1176 Time tobj = new Time();
1177 tobj.set(time);
1178 return tobj.format("%Y-%m-%d %H:%M:%S");
1179 }
Doug Zongker44f57472009-09-20 15:52:43 -07001180
Alon Alberte0bde332011-09-22 14:26:16 -07001181 protected void dumpSyncState(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001182 pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001183 pw.print("auto sync: ");
1184 List<UserInfo> users = getAllUsers();
1185 if (users != null) {
1186 for (UserInfo user : users) {
1187 pw.print("u" + user.id + "="
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001188 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001189 }
1190 pw.println();
1191 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001192 pw.print("memory low: "); pw.println(mStorageIsLow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001194 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001195
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001196 pw.print("accounts: ");
Fred Quintana53bd2522010-02-05 15:28:12 -08001197 if (accounts != INITIAL_ACCOUNTS_ARRAY) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001198 pw.println(accounts.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 } else {
Fred Quintana53bd2522010-02-05 15:28:12 -08001200 pw.println("not known yet");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 final long now = SystemClock.elapsedRealtime();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001203 pw.print("now: "); pw.print(now);
1204 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
Ashish Sharma69d95de2012-04-11 17:27:24 -07001205 pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000));
1206 pw.println(" (HH:MM:SS)");
Dianne Hackborn231cc602009-04-27 17:10:36 -07001207 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
1208 pw.println(" (HH:MM:SS)");
1209 pw.print("time spent syncing: ");
1210 pw.print(DateUtils.formatElapsedTime(
1211 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
1212 pw.print(" (HH:MM:SS), sync ");
1213 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
1214 pw.println("in progress");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 if (mSyncHandler.mAlarmScheduleTime != null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001216 pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
1217 pw.print(" (");
1218 pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
1219 pw.println(" (HH:MM:SS) from now)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 } else {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001221 pw.println("no alarm is scheduled (there had better not be any pending syncs)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 }
1223
Dianne Hackborn231cc602009-04-27 17:10:36 -07001224 pw.print("notification info: ");
Alon Alberte0bde332011-09-22 14:26:16 -07001225 final StringBuilder sb = new StringBuilder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 mSyncHandler.mSyncNotificationInfo.toString(sb);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001227 pw.println(sb.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228
Fred Quintana918339a2010-10-05 14:00:39 -07001229 pw.println();
1230 pw.println("Active Syncs: " + mActiveSyncContexts.size());
Alon Albert57286f92012-10-09 14:21:38 -07001231 final PackageManager pm = mContext.getPackageManager();
Fred Quintana918339a2010-10-05 14:00:39 -07001232 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
1233 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
1234 pw.print(" ");
1235 pw.print(DateUtils.formatElapsedTime(durationInSeconds));
1236 pw.print(" - ");
Alon Albert57286f92012-10-09 14:21:38 -07001237 pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
Fred Quintana918339a2010-10-05 14:00:39 -07001238 pw.println();
1239 }
1240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 synchronized (mSyncQueue) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001242 sb.setLength(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 mSyncQueue.dump(sb);
Matthew Williamsfa774182013-06-18 15:44:11 -07001244 // Dump Pending Operations.
1245 getSyncStorageEngine().dumpPendingOperations(sb);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 }
Matthew Williamsfa774182013-06-18 15:44:11 -07001247
Fred Quintana918339a2010-10-05 14:00:39 -07001248 pw.println();
1249 pw.print(sb.toString());
Dianne Hackborn231cc602009-04-27 17:10:36 -07001250
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001251 // join the installed sync adapter with the accounts list and emit for everything
1252 pw.println();
1253 pw.println("Sync Status");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001254 for (AccountAndUser account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -07001255 pw.printf("Account %s u%d %s\n",
1256 account.account.name, account.userId, account.account.type);
1257
1258 pw.println("=======================================================================");
1259 final PrintTable table = new PrintTable(13);
1260 table.set(0, 0,
1261 "Authority", // 0
1262 "Syncable", // 1
1263 "Enabled", // 2
1264 "Delay", // 3
1265 "Loc", // 4
1266 "Poll", // 5
1267 "Per", // 6
1268 "Serv", // 7
1269 "User", // 8
1270 "Tot", // 9
1271 "Time", // 10
1272 "Last Sync", // 11
1273 "Periodic" // 12
1274 );
1275
1276 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
1277 Lists.newArrayList();
1278 sorted.addAll(mSyncAdapters.getAllServices(account.userId));
1279 Collections.sort(sorted,
1280 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
1281 @Override
1282 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
1283 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
1284 return lhs.type.authority.compareTo(rhs.type.authority);
1285 }
1286 });
1287 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001288 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001289 continue;
1290 }
Alon Albert57286f92012-10-09 14:21:38 -07001291 int row = table.getNumRows();
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07001292 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
1293 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
Amith Yamasani04e0d262012-02-14 11:50:53 -08001294 account.account, account.userId, syncAdapterType.type.authority);
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07001295 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
1296 SyncStatusInfo status = syncAuthoritySyncStatus.second;
Alon Albert57286f92012-10-09 14:21:38 -07001297
1298 String authority = settings.authority;
1299 if (authority.length() > 50) {
1300 authority = authority.substring(authority.length() - 50);
1301 }
1302 table.set(row, 0, authority, settings.syncable, settings.enabled);
1303 table.set(row, 4,
1304 status.numSourceLocal,
1305 status.numSourcePoll,
1306 status.numSourcePeriodic,
1307 status.numSourceServer,
1308 status.numSourceUser,
1309 status.numSyncs,
1310 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
1311
1312
1313 for (int i = 0; i < settings.periodicSyncs.size(); i++) {
Matthew Williamsfa774182013-06-18 15:44:11 -07001314 final PeriodicSync sync = settings.periodicSyncs.get(i);
1315 final String period =
1316 String.format("[p:%d s, f: %d s]", sync.period, sync.flexTime);
1317 final String extras =
1318 sync.extras.size() > 0 ?
1319 sync.extras.toString() : "Bundle[]";
1320 final String next = "Next sync: " + formatTime(status.getPeriodicSyncTime(i)
1321 + sync.period * 1000);
1322 table.set(row + i * 2, 12, period + " " + extras);
Alon Albert57286f92012-10-09 14:21:38 -07001323 table.set(row + i * 2 + 1, 12, next);
1324 }
1325
1326 int row1 = row;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001327 if (settings.delayUntil > now) {
Alon Albert57286f92012-10-09 14:21:38 -07001328 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
1329 if (settings.backoffTime > now) {
1330 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
1331 table.set(row1++, 12, settings.backoffDelay / 1000);
1332 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001333 }
Alon Albert57286f92012-10-09 14:21:38 -07001334
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001335 if (status.lastSuccessTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001336 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
1337 + " " + "SUCCESS");
1338 table.set(row1++, 11, formatTime(status.lastSuccessTime));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001339 }
1340 if (status.lastFailureTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001341 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
1342 + " " + "FAILURE");
1343 table.set(row1++, 11, formatTime(status.lastFailureTime));
1344 //noinspection UnusedAssignment
1345 table.set(row1++, 11, status.lastFailureMesg);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001346 }
1347 }
Alon Albert57286f92012-10-09 14:21:38 -07001348 table.writeTo(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 }
1350 }
1351
Fabrice Di Megliodc2dd882011-01-26 14:31:48 -08001352 private String getLastFailureMessage(int code) {
1353 switch (code) {
1354 case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS:
1355 return "sync already in progress";
1356
1357 case ContentResolver.SYNC_ERROR_AUTHENTICATION:
1358 return "authentication error";
1359
1360 case ContentResolver.SYNC_ERROR_IO:
1361 return "I/O error";
1362
1363 case ContentResolver.SYNC_ERROR_PARSE:
1364 return "parse error";
1365
1366 case ContentResolver.SYNC_ERROR_CONFLICT:
1367 return "conflict error";
1368
1369 case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS:
1370 return "too many deletions error";
1371
1372 case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES:
1373 return "too many retries error";
1374
1375 case ContentResolver.SYNC_ERROR_INTERNAL:
1376 return "internal error";
1377
1378 default:
1379 return "unknown";
1380 }
1381 }
1382
Dianne Hackborn231cc602009-04-27 17:10:36 -07001383 private void dumpTimeSec(PrintWriter pw, long time) {
1384 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
1385 pw.print('s');
1386 }
Doug Zongker44f57472009-09-20 15:52:43 -07001387
Dianne Hackborn231cc602009-04-27 17:10:36 -07001388 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
1389 pw.print("Success ("); pw.print(ds.successCount);
1390 if (ds.successCount > 0) {
1391 pw.print(" for "); dumpTimeSec(pw, ds.successTime);
1392 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
1393 }
1394 pw.print(") Failure ("); pw.print(ds.failureCount);
1395 if (ds.failureCount > 0) {
1396 pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
1397 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
1398 }
1399 pw.println(")");
1400 }
Doug Zongker44f57472009-09-20 15:52:43 -07001401
Alon Alberte0bde332011-09-22 14:26:16 -07001402 protected void dumpSyncHistory(PrintWriter pw) {
1403 dumpRecentHistory(pw);
1404 dumpDayStatistics(pw);
1405 }
1406
1407 private void dumpRecentHistory(PrintWriter pw) {
1408 final ArrayList<SyncStorageEngine.SyncHistoryItem> items
1409 = mSyncStorageEngine.getSyncHistory();
1410 if (items != null && items.size() > 0) {
1411 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
1412 long totalElapsedTime = 0;
1413 long totalTimes = 0;
1414 final int N = items.size();
1415
1416 int maxAuthority = 0;
1417 int maxAccount = 0;
1418 for (SyncStorageEngine.SyncHistoryItem item : items) {
1419 SyncStorageEngine.AuthorityInfo authority
1420 = mSyncStorageEngine.getAuthority(item.authorityId);
1421 final String authorityName;
1422 final String accountKey;
1423 if (authority != null) {
1424 authorityName = authority.authority;
Alon Albert8e285552012-09-17 15:05:27 -07001425 accountKey = authority.account.name + "/" + authority.account.type
1426 + " u" + authority.userId;
Alon Alberte0bde332011-09-22 14:26:16 -07001427 } else {
1428 authorityName = "Unknown";
1429 accountKey = "Unknown";
1430 }
1431
1432 int length = authorityName.length();
1433 if (length > maxAuthority) {
1434 maxAuthority = length;
1435 }
1436 length = accountKey.length();
1437 if (length > maxAccount) {
1438 maxAccount = length;
1439 }
1440
1441 final long elapsedTime = item.elapsedTime;
1442 totalElapsedTime += elapsedTime;
1443 totalTimes++;
1444 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
1445 if (authoritySyncStats == null) {
1446 authoritySyncStats = new AuthoritySyncStats(authorityName);
1447 authorityMap.put(authorityName, authoritySyncStats);
1448 }
1449 authoritySyncStats.elapsedTime += elapsedTime;
1450 authoritySyncStats.times++;
1451 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
1452 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
1453 if (accountSyncStats == null) {
1454 accountSyncStats = new AccountSyncStats(accountKey);
1455 accountMap.put(accountKey, accountSyncStats);
1456 }
1457 accountSyncStats.elapsedTime += elapsedTime;
1458 accountSyncStats.times++;
1459
1460 }
1461
Alon Albert27096822012-01-11 18:06:41 -08001462 if (totalElapsedTime > 0) {
1463 pw.println();
1464 pw.printf("Detailed Statistics (Recent history): "
1465 + "%d (# of times) %ds (sync time)\n",
1466 totalTimes, totalElapsedTime / 1000);
Alon Alberte0bde332011-09-22 14:26:16 -07001467
Alon Albert27096822012-01-11 18:06:41 -08001468 final List<AuthoritySyncStats> sortedAuthorities =
1469 new ArrayList<AuthoritySyncStats>(authorityMap.values());
1470 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
Alon Albertbf976ba2011-10-03 13:06:43 -07001471 @Override
Alon Albert27096822012-01-11 18:06:41 -08001472 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
Alon Albertbf976ba2011-10-03 13:06:43 -07001473 // reverse order
1474 int compare = Integer.compare(rhs.times, lhs.times);
1475 if (compare == 0) {
1476 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
Alon Alberte0bde332011-09-22 14:26:16 -07001477 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001478 return compare;
Alon Alberte0bde332011-09-22 14:26:16 -07001479 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001480 });
Alon Albert27096822012-01-11 18:06:41 -08001481
1482 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
1483 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
1484 final char chars[] = new char[padLength];
1485 Arrays.fill(chars, '-');
1486 final String separator = new String(chars);
1487
1488 final String authorityFormat =
1489 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2);
1490 final String accountFormat =
1491 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength);
1492
1493 pw.println(separator);
1494 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
1495 String name = authoritySyncStats.name;
1496 long elapsedTime;
1497 int times;
1498 String timeStr;
1499 String timesStr;
1500
1501 elapsedTime = authoritySyncStats.elapsedTime;
1502 times = authoritySyncStats.times;
Alon Albertbf976ba2011-10-03 13:06:43 -07001503 timeStr = String.format("%ds/%d%%",
1504 elapsedTime / 1000,
1505 elapsedTime * 100 / totalElapsedTime);
1506 timesStr = String.format("%d/%d%%",
1507 times,
1508 times * 100 / totalTimes);
Alon Albert27096822012-01-11 18:06:41 -08001509 pw.printf(authorityFormat, name, timesStr, timeStr);
1510
1511 final List<AccountSyncStats> sortedAccounts =
1512 new ArrayList<AccountSyncStats>(
1513 authoritySyncStats.accountMap.values());
1514 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
1515 @Override
1516 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
1517 // reverse order
1518 int compare = Integer.compare(rhs.times, lhs.times);
1519 if (compare == 0) {
1520 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
1521 }
1522 return compare;
1523 }
1524 });
1525 for (AccountSyncStats stats: sortedAccounts) {
1526 elapsedTime = stats.elapsedTime;
1527 times = stats.times;
1528 timeStr = String.format("%ds/%d%%",
1529 elapsedTime / 1000,
1530 elapsedTime * 100 / totalElapsedTime);
1531 timesStr = String.format("%d/%d%%",
1532 times,
1533 times * 100 / totalTimes);
1534 pw.printf(accountFormat, stats.name, timesStr, timeStr);
1535 }
1536 pw.println(separator);
Alon Alberte0bde332011-09-22 14:26:16 -07001537 }
Alon Alberte0bde332011-09-22 14:26:16 -07001538 }
1539
1540 pw.println();
1541 pw.println("Recent Sync History");
Alon Albert57286f92012-10-09 14:21:38 -07001542 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n";
Alon Albertbf976ba2011-10-03 13:06:43 -07001543 final Map<String, Long> lastTimeMap = Maps.newHashMap();
Alon Albert57286f92012-10-09 14:21:38 -07001544 final PackageManager pm = mContext.getPackageManager();
Alon Alberte0bde332011-09-22 14:26:16 -07001545 for (int i = 0; i < N; i++) {
1546 SyncStorageEngine.SyncHistoryItem item = items.get(i);
1547 SyncStorageEngine.AuthorityInfo authority
1548 = mSyncStorageEngine.getAuthority(item.authorityId);
1549 final String authorityName;
1550 final String accountKey;
1551 if (authority != null) {
1552 authorityName = authority.authority;
Alon Albert8e285552012-09-17 15:05:27 -07001553 accountKey = authority.account.name + "/" + authority.account.type
1554 + " u" + authority.userId;
Alon Alberte0bde332011-09-22 14:26:16 -07001555 } else {
1556 authorityName = "Unknown";
1557 accountKey = "Unknown";
1558 }
1559 final long elapsedTime = item.elapsedTime;
1560 final Time time = new Time();
1561 final long eventTime = item.eventTime;
1562 time.set(eventTime);
1563
Alon Albertbf976ba2011-10-03 13:06:43 -07001564 final String key = authorityName + "/" + accountKey;
1565 final Long lastEventTime = lastTimeMap.get(key);
1566 final String diffString;
1567 if (lastEventTime == null) {
1568 diffString = "";
1569 } else {
1570 final long diff = (lastEventTime - eventTime) / 1000;
1571 if (diff < 60) {
1572 diffString = String.valueOf(diff);
1573 } else if (diff < 3600) {
1574 diffString = String.format("%02d:%02d", diff / 60, diff % 60);
1575 } else {
1576 final long sec = diff % 3600;
1577 diffString = String.format("%02d:%02d:%02d",
1578 diff / 3600, sec / 60, sec % 60);
1579 }
1580 }
1581 lastTimeMap.put(key, eventTime);
1582
1583 pw.printf(" #%-3d: %s %8s %5.1fs %8s",
Alon Alberte0bde332011-09-22 14:26:16 -07001584 i + 1,
1585 formatTime(eventTime),
1586 SyncStorageEngine.SOURCES[item.source],
Alon Albertbf976ba2011-10-03 13:06:43 -07001587 ((float) elapsedTime) / 1000,
1588 diffString);
Alon Albert57286f92012-10-09 14:21:38 -07001589 pw.printf(format, accountKey, authorityName,
1590 SyncOperation.reasonToString(pm, item.reason));
Alon Alberte0bde332011-09-22 14:26:16 -07001591
1592 if (item.event != SyncStorageEngine.EVENT_STOP
1593 || item.upstreamActivity != 0
1594 || item.downstreamActivity != 0) {
1595 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n",
1596 item.event,
1597 item.upstreamActivity,
1598 item.downstreamActivity);
1599 }
1600 if (item.mesg != null
1601 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
1602 pw.printf(" mesg=%s\n", item.mesg);
1603 }
1604 }
Alon Albert57286f92012-10-09 14:21:38 -07001605 pw.println();
1606 pw.println("Recent Sync History Extras");
1607 for (int i = 0; i < N; i++) {
1608 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
1609 final Bundle extras = item.extras;
1610 if (extras == null || extras.size() == 0) {
1611 continue;
1612 }
1613 final SyncStorageEngine.AuthorityInfo authority
1614 = mSyncStorageEngine.getAuthority(item.authorityId);
1615 final String authorityName;
1616 final String accountKey;
1617 if (authority != null) {
1618 authorityName = authority.authority;
1619 accountKey = authority.account.name + "/" + authority.account.type
1620 + " u" + authority.userId;
1621 } else {
1622 authorityName = "Unknown";
1623 accountKey = "Unknown";
1624 }
1625 final Time time = new Time();
1626 final long eventTime = item.eventTime;
1627 time.set(eventTime);
1628
1629 pw.printf(" #%-3d: %s %8s ",
1630 i + 1,
1631 formatTime(eventTime),
1632 SyncStorageEngine.SOURCES[item.source]);
1633
1634 pw.printf(format, accountKey, authorityName, extras);
1635 }
Alon Alberte0bde332011-09-22 14:26:16 -07001636 }
1637 }
1638
1639 private void dumpDayStatistics(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001640 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
1641 if (dses != null && dses[0] != null) {
1642 pw.println();
1643 pw.println("Sync Statistics");
1644 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]);
1645 int today = dses[0].day;
1646 int i;
1647 SyncStorageEngine.DayStats ds;
Doug Zongker44f57472009-09-20 15:52:43 -07001648
Dianne Hackborn231cc602009-04-27 17:10:36 -07001649 // Print each day in the current week.
1650 for (i=1; i<=6 && i < dses.length; i++) {
1651 ds = dses[i];
1652 if (ds == null) break;
1653 int delta = today-ds.day;
1654 if (delta > 6) break;
Doug Zongker44f57472009-09-20 15:52:43 -07001655
Dianne Hackborn231cc602009-04-27 17:10:36 -07001656 pw.print(" Day-"); pw.print(delta); pw.print(": ");
1657 dumpDayStatistic(pw, ds);
1658 }
Doug Zongker44f57472009-09-20 15:52:43 -07001659
Dianne Hackborn231cc602009-04-27 17:10:36 -07001660 // Aggregate all following days into weeks and print totals.
1661 int weekDay = today;
1662 while (i < dses.length) {
1663 SyncStorageEngine.DayStats aggr = null;
1664 weekDay -= 7;
1665 while (i < dses.length) {
1666 ds = dses[i];
1667 if (ds == null) {
1668 i = dses.length;
1669 break;
1670 }
1671 int delta = weekDay-ds.day;
1672 if (delta > 6) break;
1673 i++;
Doug Zongker44f57472009-09-20 15:52:43 -07001674
Dianne Hackborn231cc602009-04-27 17:10:36 -07001675 if (aggr == null) {
1676 aggr = new SyncStorageEngine.DayStats(weekDay);
1677 }
1678 aggr.successCount += ds.successCount;
1679 aggr.successTime += ds.successTime;
1680 aggr.failureCount += ds.failureCount;
1681 aggr.failureTime += ds.failureTime;
1682 }
1683 if (aggr != null) {
1684 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": ");
1685 dumpDayStatistic(pw, aggr);
1686 }
1687 }
1688 }
Alon Alberte0bde332011-09-22 14:26:16 -07001689 }
Doug Zongker44f57472009-09-20 15:52:43 -07001690
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001691 private void dumpSyncAdapters(IndentingPrintWriter pw) {
1692 pw.println();
1693 final List<UserInfo> users = getAllUsers();
1694 if (users != null) {
1695 for (UserInfo user : users) {
1696 pw.println("Sync adapters for " + user + ":");
1697 pw.increaseIndent();
1698 for (RegisteredServicesCache.ServiceInfo<?> info :
1699 mSyncAdapters.getAllServices(user.id)) {
1700 pw.println(info);
1701 }
1702 pw.decreaseIndent();
1703 pw.println();
1704 }
1705 }
1706 }
1707
Alon Alberte0bde332011-09-22 14:26:16 -07001708 private static class AuthoritySyncStats {
1709 String name;
1710 long elapsedTime;
1711 int times;
1712 Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
1713
1714 private AuthoritySyncStats(String name) {
1715 this.name = name;
1716 }
1717 }
1718
1719 private static class AccountSyncStats {
1720 String name;
1721 long elapsedTime;
1722 int times;
1723
1724 private AccountSyncStats(String name) {
1725 this.name = name;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001726 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 }
1728
1729 /**
1730 * A helper object to keep track of the time we have spent syncing since the last boot
1731 */
1732 private class SyncTimeTracker {
1733 /** True if a sync was in progress on the most recent call to update() */
1734 boolean mLastWasSyncing = false;
1735 /** Used to track when lastWasSyncing was last set */
1736 long mWhenSyncStarted = 0;
1737 /** The cumulative time we have spent syncing */
1738 private long mTimeSpentSyncing;
1739
1740 /** Call to let the tracker know that the sync state may have changed */
1741 public synchronized void update() {
Fred Quintana918339a2010-10-05 14:00:39 -07001742 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 if (isSyncInProgress == mLastWasSyncing) return;
1744 final long now = SystemClock.elapsedRealtime();
1745 if (isSyncInProgress) {
1746 mWhenSyncStarted = now;
1747 } else {
1748 mTimeSpentSyncing += now - mWhenSyncStarted;
1749 }
1750 mLastWasSyncing = isSyncInProgress;
1751 }
1752
1753 /** Get how long we have been syncing, in ms */
1754 public synchronized long timeSpentSyncing() {
1755 if (!mLastWasSyncing) return mTimeSpentSyncing;
1756
1757 final long now = SystemClock.elapsedRealtime();
1758 return mTimeSpentSyncing + (now - mWhenSyncStarted);
1759 }
1760 }
1761
Fred Quintana718d8a22009-04-29 17:53:20 -07001762 class ServiceConnectionData {
1763 public final ActiveSyncContext activeSyncContext;
1764 public final ISyncAdapter syncAdapter;
1765 ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
1766 this.activeSyncContext = activeSyncContext;
1767 this.syncAdapter = syncAdapter;
1768 }
1769 }
1770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 /**
1772 * Handles SyncOperation Messages that are posted to the associated
1773 * HandlerThread.
1774 */
1775 class SyncHandler extends Handler {
1776 // Messages that can be sent on mHandler
1777 private static final int MESSAGE_SYNC_FINISHED = 1;
1778 private static final int MESSAGE_SYNC_ALARM = 2;
1779 private static final int MESSAGE_CHECK_ALARMS = 3;
Fred Quintana718d8a22009-04-29 17:53:20 -07001780 private static final int MESSAGE_SERVICE_CONNECTED = 4;
1781 private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
Fred Quintana918339a2010-10-05 14:00:39 -07001782 private static final int MESSAGE_CANCEL = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783
1784 public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
1785 private Long mAlarmScheduleTime = null;
1786 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001787 private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks =
Fred Quintanab3029c32010-04-06 13:27:12 -07001788 Maps.newHashMap();
Matthew Williams8704fc32013-09-27 11:32:35 -07001789 private List<Message> mBootQueue = new ArrayList<Message>();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001790
Fred Quintanae91ebe22009-09-29 20:44:30 -07001791 public void onBootCompleted() {
Matthew Williams8704fc32013-09-27 11:32:35 -07001792 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1793 Log.v(TAG, "Boot completed, clearing boot queue.");
1794 }
Jeff Sharkey8f55d112012-10-11 11:00:21 -07001795 doDatabaseCleanup();
Matthew Williams8704fc32013-09-27 11:32:35 -07001796 synchronized(this) {
1797 // Dispatch any stashed messages.
1798 for (Message message : mBootQueue) {
1799 sendMessage(message);
1800 }
1801 mBootQueue = null;
1802 mBootCompleted = true;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001803 }
1804 }
1805
Fred Quintanafdb2dca2011-09-08 15:59:56 -07001806 private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) {
1807 final Pair<Account, String> wakeLockKey = Pair.create(account, authority);
Fred Quintanab3029c32010-04-06 13:27:12 -07001808 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
1809 if (wakeLock == null) {
Dianne Hackborn13ac0412013-06-25 19:34:49 -07001810 final String name = SYNC_WAKE_LOCK_PREFIX + "/" + authority + "/" + account.type
1811 + "/" + account.name;
Fred Quintanab3029c32010-04-06 13:27:12 -07001812 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
1813 wakeLock.setReferenceCounted(false);
1814 mWakeLocks.put(wakeLockKey, wakeLock);
1815 }
1816 return wakeLock;
1817 }
1818
Matthew Williams8704fc32013-09-27 11:32:35 -07001819 /**
1820 * Stash any messages that come to the handler before boot is complete.
1821 * {@link #onBootCompleted()} will disable this and dispatch all the messages collected.
1822 * @param msg Message to dispatch at a later point.
1823 * @return true if a message was enqueued, false otherwise. This is to avoid losing the
1824 * message if we manage to acquire the lock but by the time we do boot has completed.
1825 */
1826 private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
1827 synchronized (this) {
1828 if (!mBootCompleted) {
1829 // Need to copy the message bc looper will recycle it.
1830 mBootQueue.add(Message.obtain(msg));
1831 return true;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001832 }
Matthew Williams8704fc32013-09-27 11:32:35 -07001833 return false;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001834 }
1835 }
Matthew Williams8704fc32013-09-27 11:32:35 -07001836
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001837 /**
1838 * Used to keep track of whether a sync notification is active and who it is for.
1839 */
1840 class SyncNotificationInfo {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 // true iff the notification manager has been asked to send the notification
1842 public boolean isActive = false;
1843
1844 // Set when we transition from not running a sync to running a sync, and cleared on
1845 // the opposite transition.
1846 public Long startTime = null;
1847
1848 public void toString(StringBuilder sb) {
Fred Quintana918339a2010-10-05 14:00:39 -07001849 sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 }
1851
1852 @Override
1853 public String toString() {
1854 StringBuilder sb = new StringBuilder();
1855 toString(sb);
1856 return sb.toString();
1857 }
1858 }
1859
1860 public SyncHandler(Looper looper) {
1861 super(looper);
1862 }
1863
Matthew Williamsfa774182013-06-18 15:44:11 -07001864 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001865 public void handleMessage(Message msg) {
Matthew Williams8704fc32013-09-27 11:32:35 -07001866 if (tryEnqueueMessageUntilReadyToRun(msg)) {
1867 return;
1868 }
1869
Fred Quintana918339a2010-10-05 14:00:39 -07001870 long earliestFuturePollTime = Long.MAX_VALUE;
1871 long nextPendingSyncTime = Long.MAX_VALUE;
Subir Jhanb753c57b2011-01-10 12:09:26 -08001872 // Setting the value here instead of a method because we want the dumpsys logs
1873 // to have the most recent value used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 try {
Alon Albert1bad83a2011-02-16 10:29:56 -08001875 mDataConnectionIsConnected = readDataConnectionState();
Fred Quintana918339a2010-10-05 14:00:39 -07001876 mSyncManagerWakeLock.acquire();
Fred Quintana77c560f2010-03-29 22:20:26 -07001877 // Always do this first so that we be sure that any periodic syncs that
1878 // are ready to run have been converted into pending syncs. This allows the
1879 // logic that considers the next steps to take based on the set of pending syncs
1880 // to also take into account the periodic syncs.
1881 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 switch (msg.what) {
Fred Quintana918339a2010-10-05 14:00:39 -07001883 case SyncHandler.MESSAGE_CANCEL: {
Matthew Williamsfa774182013-06-18 15:44:11 -07001884 Pair<Account, String> payload = (Pair<Account, String>) msg.obj;
Fred Quintana918339a2010-10-05 14:00:39 -07001885 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1886 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
1887 + payload.first + ", " + payload.second);
1888 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001889 cancelActiveSyncLocked(payload.first, msg.arg1, payload.second);
Fred Quintana918339a2010-10-05 14:00:39 -07001890 nextPendingSyncTime = maybeStartNextSyncLocked();
1891 break;
1892 }
1893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 case SyncHandler.MESSAGE_SYNC_FINISHED:
1895 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1896 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
1897 }
1898 SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj;
Fred Quintana918339a2010-10-05 14:00:39 -07001899 if (!isSyncStillActive(payload.activeSyncContext)) {
1900 Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
1901 + "sync is no longer active: "
1902 + payload.activeSyncContext);
1903 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 }
Fred Quintana918339a2010-10-05 14:00:39 -07001905 runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906
Fred Quintana918339a2010-10-05 14:00:39 -07001907 // since a sync just finished check if it is time to start a new sync
1908 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 break;
1910
Fred Quintana718d8a22009-04-29 17:53:20 -07001911 case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
1912 ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
1913 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1914 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07001915 + msgData.activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001916 }
1917 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07001918 if (isSyncStillActive(msgData.activeSyncContext)) {
1919 runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
Fred Quintana718d8a22009-04-29 17:53:20 -07001920 }
1921 break;
1922 }
1923
1924 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
Fred Quintana918339a2010-10-05 14:00:39 -07001925 final ActiveSyncContext currentSyncContext =
1926 ((ServiceConnectionData)msg.obj).activeSyncContext;
Fred Quintana718d8a22009-04-29 17:53:20 -07001927 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1928 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07001929 + currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001930 }
1931 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07001932 if (isSyncStillActive(currentSyncContext)) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001933 // cancel the sync if we have a syncadapter, which means one is
1934 // outstanding
Fred Quintana918339a2010-10-05 14:00:39 -07001935 if (currentSyncContext.mSyncAdapter != null) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001936 try {
Fred Quintana918339a2010-10-05 14:00:39 -07001937 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001938 } catch (RemoteException e) {
1939 // we don't need to retry this in this case
1940 }
1941 }
1942
1943 // pretend that the sync failed with an IOException,
1944 // which is a soft error
1945 SyncResult syncResult = new SyncResult();
1946 syncResult.stats.numIoExceptions++;
Fred Quintana918339a2010-10-05 14:00:39 -07001947 runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07001948
Fred Quintana918339a2010-10-05 14:00:39 -07001949 // since a sync just finished check if it is time to start a new sync
1950 nextPendingSyncTime = maybeStartNextSyncLocked();
Fred Quintana718d8a22009-04-29 17:53:20 -07001951 }
1952
1953 break;
1954 }
1955
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 case SyncHandler.MESSAGE_SYNC_ALARM: {
1957 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
1958 if (isLoggable) {
1959 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM");
1960 }
1961 mAlarmScheduleTime = null;
1962 try {
Fred Quintana918339a2010-10-05 14:00:39 -07001963 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 } finally {
1965 mHandleAlarmWakeLock.release();
1966 }
1967 break;
1968 }
1969
1970 case SyncHandler.MESSAGE_CHECK_ALARMS:
1971 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1972 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
1973 }
Fred Quintana918339a2010-10-05 14:00:39 -07001974 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 break;
1976 }
1977 } finally {
Fred Quintana918339a2010-10-05 14:00:39 -07001978 manageSyncNotificationLocked();
1979 manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 mSyncTimeTracker.update();
Fred Quintana918339a2010-10-05 14:00:39 -07001981 mSyncManagerWakeLock.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 }
1983 }
1984
Fred Quintana77c560f2010-03-29 22:20:26 -07001985 /**
1986 * Turn any periodic sync operations that are ready to run into pending sync operations.
1987 * @return the desired start time of the earliest future periodic sync operation,
1988 * in milliseconds since boot
1989 */
Fred Quintana918339a2010-10-05 14:00:39 -07001990 private long scheduleReadyPeriodicSyncs() {
Matthew Williamsfa774182013-06-18 15:44:11 -07001991 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
1992 if (isLoggable) {
1993 Log.v(TAG, "scheduleReadyPeriodicSyncs");
1994 }
Fred Quintana77c560f2010-03-29 22:20:26 -07001995 final boolean backgroundDataUsageAllowed =
1996 getConnectivityManager().getBackgroundDataSetting();
Fred Quintana918339a2010-10-05 14:00:39 -07001997 long earliestFuturePollTime = Long.MAX_VALUE;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001998 if (!backgroundDataUsageAllowed) {
Fred Quintana77c560f2010-03-29 22:20:26 -07001999 return earliestFuturePollTime;
2000 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002001
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002002 AccountAndUser[] accounts = mRunningAccounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002003
Fred Quintana77c560f2010-03-29 22:20:26 -07002004 final long nowAbsolute = System.currentTimeMillis();
Ashish Sharma69d95de2012-04-11 17:27:24 -07002005 final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002006 ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
Ashish Sharma69d95de2012-04-11 17:27:24 -07002007
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002008 ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos = mSyncStorageEngine
2009 .getCopyOfAllAuthoritiesWithSyncStatus();
2010 for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) {
2011 final AuthorityInfo authorityInfo = info.first;
2012 final SyncStatusInfo status = info.second;
Matthew Williams632515b2013-10-10 15:51:00 -07002013 if (TextUtils.isEmpty(authorityInfo.authority)) {
2014 Log.e(TAG, "Got an empty provider string. Skipping: " + authorityInfo);
2015 continue;
2016 }
2017 // skip the sync if the account of this operation no longer exists
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002018 if (!containsAccountAndUser(
2019 accounts, authorityInfo.account, authorityInfo.userId)) {
Fred Quintana77c560f2010-03-29 22:20:26 -07002020 continue;
2021 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002022
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002023 if (!mSyncStorageEngine.getMasterSyncAutomatically(authorityInfo.userId)
2024 || !mSyncStorageEngine.getSyncAutomatically(
2025 authorityInfo.account, authorityInfo.userId,
2026 authorityInfo.authority)) {
Fred Quintana77c560f2010-03-29 22:20:26 -07002027 continue;
2028 }
2029
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002030 if (getIsSyncable(
2031 authorityInfo.account, authorityInfo.userId, authorityInfo.authority)
Amith Yamasani04e0d262012-02-14 11:50:53 -08002032 == 0) {
Fred Quintana77c560f2010-03-29 22:20:26 -07002033 continue;
2034 }
2035
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002036 for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) {
Matthew Williamsfa774182013-06-18 15:44:11 -07002037 final PeriodicSync sync = authorityInfo.periodicSyncs.get(i);
2038 final Bundle extras = sync.extras;
Matthew Williams62222882013-08-20 15:32:20 -07002039 final long periodInMillis = sync.period * 1000;
2040 final long flexInMillis = sync.flexTime * 1000;
Matthew Williamsfa774182013-06-18 15:44:11 -07002041 // Skip if the period is invalid.
Ashish Sharma49709742013-06-10 11:25:51 -07002042 if (periodInMillis <= 0) {
2043 continue;
2044 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002045 // Find when this periodic sync was last scheduled to run.
Fred Quintana77c560f2010-03-29 22:20:26 -07002046 final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
Ashish Sharma69d95de2012-04-11 17:27:24 -07002047 long remainingMillis
Matthew Williamsfa774182013-06-18 15:44:11 -07002048 = periodInMillis - (shiftedNowAbsolute % periodInMillis);
2049 long timeSinceLastRunMillis
2050 = (nowAbsolute - lastPollTimeAbsolute);
2051 // Schedule this periodic sync to run early if it's close enough to its next
2052 // runtime, and far enough from its last run time.
2053 // If we are early, there will still be time remaining in this period.
2054 boolean runEarly = remainingMillis <= flexInMillis
2055 && timeSinceLastRunMillis > periodInMillis - flexInMillis;
2056 if (isLoggable) {
2057 Log.v(TAG, "sync: " + i + " for " + authorityInfo.authority + "."
2058 + " period: " + (periodInMillis)
2059 + " flex: " + (flexInMillis)
2060 + " remaining: " + (remainingMillis)
2061 + " time_since_last: " + timeSinceLastRunMillis
2062 + " last poll absol: " + lastPollTimeAbsolute
Matthew Williamsfa774182013-06-18 15:44:11 -07002063 + " shifted now: " + shiftedNowAbsolute
2064 + " run_early: " + runEarly);
2065 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07002066 /*
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002067 * Sync scheduling strategy: Set the next periodic sync
2068 * based on a random offset (in seconds). Also sync right
2069 * now if any of the following cases hold and mark it as
2070 * having been scheduled
Matthew Williamsfa774182013-06-18 15:44:11 -07002071 * Case 1: This sync is ready to run now.
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002072 * Case 2: If the lastPollTimeAbsolute is in the
2073 * future, sync now and reinitialize. This can happen for
2074 * example if the user changed the time, synced and changed
2075 * back.
2076 * Case 3: If we failed to sync at the last scheduled
Matthew Williamsfa774182013-06-18 15:44:11 -07002077 * time.
2078 * Case 4: This sync is close enough to the time that we can schedule it.
Ashish Sharma69d95de2012-04-11 17:27:24 -07002079 */
Matthew Williamsfa774182013-06-18 15:44:11 -07002080 if (runEarly // Case 4
2081 || remainingMillis == periodInMillis // Case 1
Ashish Sharma69d95de2012-04-11 17:27:24 -07002082 || lastPollTimeAbsolute > nowAbsolute // Case 2
Matthew Williamsfa774182013-06-18 15:44:11 -07002083 || timeSinceLastRunMillis >= periodInMillis) { // Case 3
Ashish Sharma69d95de2012-04-11 17:27:24 -07002084 // Sync now
Matthew Williamsfa774182013-06-18 15:44:11 -07002085
Amith Yamasani04e0d262012-02-14 11:50:53 -08002086 final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002087 authorityInfo.account, authorityInfo.userId,
2088 authorityInfo.authority);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002089 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2090 syncAdapterInfo = mSyncAdapters.getServiceInfo(
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002091 SyncAdapterType.newKey(
2092 authorityInfo.authority, authorityInfo.account.type),
2093 authorityInfo.userId);
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002094 if (syncAdapterInfo == null) {
2095 continue;
2096 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002097 mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident,
2098 authorityInfo.periodicSyncs.get(i), nowAbsolute);
Fred Quintana77c560f2010-03-29 22:20:26 -07002099 scheduleSyncOperation(
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002100 new SyncOperation(authorityInfo.account, authorityInfo.userId,
Alon Albert57286f92012-10-09 14:21:38 -07002101 SyncOperation.REASON_PERIODIC,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002102 SyncStorageEngine.SOURCE_PERIODIC,
Matthew Williamsfa774182013-06-18 15:44:11 -07002103 authorityInfo.authority, extras,
2104 0 /* runtime */, 0 /* flex */,
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002105 backoff != null ? backoff.first : 0,
Fred Quintana918339a2010-10-05 14:00:39 -07002106 mSyncStorageEngine.getDelayUntilTime(
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002107 authorityInfo.account, authorityInfo.userId,
2108 authorityInfo.authority),
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002109 syncAdapterInfo.type.allowParallelSyncs()));
Matthew Williamsfa774182013-06-18 15:44:11 -07002110
Ashish Sharma69d95de2012-04-11 17:27:24 -07002111 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002112 // Compute when this periodic sync should next run.
2113 long nextPollTimeAbsolute;
2114 if (runEarly) {
2115 // Add the time remaining so we don't get out of phase.
2116 nextPollTimeAbsolute = nowAbsolute + periodInMillis + remainingMillis;
2117 } else {
2118 nextPollTimeAbsolute = nowAbsolute + remainingMillis;
2119 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07002120 if (nextPollTimeAbsolute < earliestFuturePollTime) {
2121 earliestFuturePollTime = nextPollTimeAbsolute;
Fred Quintana77c560f2010-03-29 22:20:26 -07002122 }
2123 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002124 }
2125
Fred Quintana918339a2010-10-05 14:00:39 -07002126 if (earliestFuturePollTime == Long.MAX_VALUE) {
2127 return Long.MAX_VALUE;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002128 }
2129
Fred Quintana77c560f2010-03-29 22:20:26 -07002130 // convert absolute time to elapsed time
Matthew Williamsfa774182013-06-18 15:44:11 -07002131 return SystemClock.elapsedRealtime() +
2132 ((earliestFuturePollTime < nowAbsolute) ?
2133 0 : (earliestFuturePollTime - nowAbsolute));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002134 }
2135
Fred Quintana918339a2010-10-05 14:00:39 -07002136 private long maybeStartNextSyncLocked() {
2137 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2138 if (isLoggable) Log.v(TAG, "maybeStartNextSync");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139
2140 // If we aren't ready to run (e.g. the data connection is down), get out.
2141 if (!mDataConnectionIsConnected) {
2142 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002143 Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002144 }
Fred Quintana918339a2010-10-05 14:00:39 -07002145 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146 }
2147
2148 if (mStorageIsLow) {
2149 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002150 Log.v(TAG, "maybeStartNextSync: memory low, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 }
Fred Quintana918339a2010-10-05 14:00:39 -07002152 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002153 }
2154
2155 // If the accounts aren't known yet then we aren't ready to run. We will be kicked
2156 // when the account lookup request does complete.
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002157 AccountAndUser[] accounts = mRunningAccounts;
Fred Quintana53bd2522010-02-05 15:28:12 -08002158 if (accounts == INITIAL_ACCOUNTS_ARRAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002159 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002160 Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 }
Fred Quintana918339a2010-10-05 14:00:39 -07002162 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 }
2164
2165 // Otherwise consume SyncOperations from the head of the SyncQueue until one is
2166 // found that is runnable (not disabled, etc). If that one is ready to run then
2167 // start it, otherwise just get out.
Fred Quintanaf892fb32009-08-27 21:32:08 -07002168 final boolean backgroundDataUsageAllowed =
2169 getConnectivityManager().getBackgroundDataSetting();
Fred Quintana77c560f2010-03-29 22:20:26 -07002170
Fred Quintana918339a2010-10-05 14:00:39 -07002171 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172
Fred Quintana918339a2010-10-05 14:00:39 -07002173 // will be set to the next time that a sync should be considered for running
2174 long nextReadyToRunTime = Long.MAX_VALUE;
2175
2176 // order the sync queue, dropping syncs that are not allowed
2177 ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
2178 synchronized (mSyncQueue) {
2179 if (isLoggable) {
2180 Log.v(TAG, "build the operation array, syncQueue size is "
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002181 + mSyncQueue.getOperations().size());
Fred Quintana918339a2010-10-05 14:00:39 -07002182 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002183 final Iterator<SyncOperation> operationIterator =
2184 mSyncQueue.getOperations().iterator();
Alon Albert8e285552012-09-17 15:05:27 -07002185
2186 final ActivityManager activityManager
2187 = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
2188 final Set<Integer> removedUsers = Sets.newHashSet();
Fred Quintana918339a2010-10-05 14:00:39 -07002189 while (operationIterator.hasNext()) {
2190 final SyncOperation op = operationIterator.next();
Fred Quintana307da1a2010-01-21 14:24:20 -08002191
Matthew Williamsfa774182013-06-18 15:44:11 -07002192 // Drop the sync if the account of this operation no longer exists.
Amith Yamasani04e0d262012-02-14 11:50:53 -08002193 if (!containsAccountAndUser(accounts, op.account, op.userId)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002194 operationIterator.remove();
2195 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
Matthew Williamsfa774182013-06-18 15:44:11 -07002196 if (isLoggable) {
2197 Log.v(TAG, " Dropping sync operation: account doesn't exist.");
2198 }
Fred Quintana77c560f2010-03-29 22:20:26 -07002199 continue;
2200 }
2201
Matthew Williamsfa774182013-06-18 15:44:11 -07002202 // Drop this sync request if it isn't syncable.
Amith Yamasani9422bdc2013-04-10 16:58:19 -07002203 int syncableState = getIsSyncable(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002204 op.account, op.userId, op.authority);
Fred Quintana77c560f2010-03-29 22:20:26 -07002205 if (syncableState == 0) {
Fred Quintana918339a2010-10-05 14:00:39 -07002206 operationIterator.remove();
2207 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
Matthew Williamsfa774182013-06-18 15:44:11 -07002208 if (isLoggable) {
2209 Log.v(TAG, " Dropping sync operation: isSyncable == 0.");
2210 }
Fred Quintana918339a2010-10-05 14:00:39 -07002211 continue;
2212 }
2213
Matthew Williamsfa774182013-06-18 15:44:11 -07002214 // If the user is not running, drop the request.
Alon Albert8e285552012-09-17 15:05:27 -07002215 if (!activityManager.isUserRunning(op.userId)) {
2216 final UserInfo userInfo = mUserManager.getUserInfo(op.userId);
2217 if (userInfo == null) {
2218 removedUsers.add(op.userId);
2219 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002220 if (isLoggable) {
2221 Log.v(TAG, " Dropping sync operation: user not running.");
Fred Quintana918339a2010-10-05 14:00:39 -07002222 }
Fred Quintana77c560f2010-03-29 22:20:26 -07002223 continue;
2224 }
2225
Matthew Williamsfa774182013-06-18 15:44:11 -07002226 // If the next run time is in the future, even given the flexible scheduling,
2227 // return the time.
2228 if (op.effectiveRunTime - op.flexTime > now) {
2229 if (nextReadyToRunTime > op.effectiveRunTime) {
2230 nextReadyToRunTime = op.effectiveRunTime;
2231 }
2232 if (isLoggable) {
2233 Log.v(TAG, " Dropping sync operation: Sync too far in future.");
2234 }
2235 continue;
2236 }
Matthew Williams62222882013-08-20 15:32:20 -07002237
2238 // If the op isn't allowed on metered networks and we're on one, drop it.
2239 if (getConnectivityManager().isActiveNetworkMetered()
2240 && op.isMeteredDisallowed()) {
2241 operationIterator.remove();
2242 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
2243 continue;
2244 }
2245
Matthew Williamsfa774182013-06-18 15:44:11 -07002246 // TODO: change this behaviour for non-registered syncs.
Jeff Sharkey34821882011-07-13 10:44:07 -07002247 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2248 syncAdapterInfo = mSyncAdapters.getServiceInfo(
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002249 SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
Jeff Sharkey34821882011-07-13 10:44:07 -07002250
2251 // only proceed if network is connected for requesting UID
2252 final boolean uidNetworkConnected;
2253 if (syncAdapterInfo != null) {
2254 final NetworkInfo networkInfo = getConnectivityManager()
2255 .getActiveNetworkInfoForUid(syncAdapterInfo.uid);
2256 uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
2257 } else {
2258 uidNetworkConnected = false;
2259 }
2260
2261 // skip the sync if it isn't manual, and auto sync or
2262 // background data usage is disabled or network is
2263 // disconnected for the target UID.
Fred Quintana77c560f2010-03-29 22:20:26 -07002264 if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
2265 && (syncableState > 0)
Amith Yamasani04e0d262012-02-14 11:50:53 -08002266 && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId)
Fred Quintana77c560f2010-03-29 22:20:26 -07002267 || !backgroundDataUsageAllowed
Jeff Sharkey34821882011-07-13 10:44:07 -07002268 || !uidNetworkConnected
Fred Quintana77c560f2010-03-29 22:20:26 -07002269 || !mSyncStorageEngine.getSyncAutomatically(
Amith Yamasani04e0d262012-02-14 11:50:53 -08002270 op.account, op.userId, op.authority))) {
Fred Quintana918339a2010-10-05 14:00:39 -07002271 operationIterator.remove();
2272 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
Fred Quintana307da1a2010-01-21 14:24:20 -08002273 continue;
2274 }
2275
Fred Quintana918339a2010-10-05 14:00:39 -07002276 operations.add(op);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002277 }
Alon Albert8e285552012-09-17 15:05:27 -07002278 for (Integer user : removedUsers) {
2279 // if it's still removed
2280 if (mUserManager.getUserInfo(user) == null) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002281 onUserRemoved(user);
Alon Albert8e285552012-09-17 15:05:27 -07002282 }
2283 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002284 }
2285
Fred Quintana918339a2010-10-05 14:00:39 -07002286 // find the next operation to dispatch, if one is ready
Matthew Williamsfa774182013-06-18 15:44:11 -07002287 // iterate from the top, keep issuing (while potentially canceling existing syncs)
Fred Quintana918339a2010-10-05 14:00:39 -07002288 // until the quotas are filled.
2289 // once the quotas are filled iterate once more to find when the next one would be
2290 // (also considering pre-emption reasons).
2291 if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
2292 Collections.sort(operations);
2293 if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
2294 for (int i = 0, N = operations.size(); i < N; i++) {
2295 final SyncOperation candidate = operations.get(i);
2296 final boolean candidateIsInitialization = candidate.isInitialization();
2297
2298 int numInit = 0;
2299 int numRegular = 0;
2300 ActiveSyncContext conflict = null;
2301 ActiveSyncContext longRunning = null;
2302 ActiveSyncContext toReschedule = null;
Fred Quintanadc475562012-05-04 15:51:54 -07002303 ActiveSyncContext oldestNonExpeditedRegular = null;
Fred Quintana918339a2010-10-05 14:00:39 -07002304
2305 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2306 final SyncOperation activeOp = activeSyncContext.mSyncOperation;
2307 if (activeOp.isInitialization()) {
2308 numInit++;
2309 } else {
2310 numRegular++;
Fred Quintanadc475562012-05-04 15:51:54 -07002311 if (!activeOp.isExpedited()) {
2312 if (oldestNonExpeditedRegular == null
2313 || (oldestNonExpeditedRegular.mStartTime
2314 > activeSyncContext.mStartTime)) {
2315 oldestNonExpeditedRegular = activeSyncContext;
2316 }
2317 }
Fred Quintana918339a2010-10-05 14:00:39 -07002318 }
2319 if (activeOp.account.type.equals(candidate.account.type)
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002320 && activeOp.authority.equals(candidate.authority)
Amith Yamasani04e0d262012-02-14 11:50:53 -08002321 && activeOp.userId == candidate.userId
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002322 && (!activeOp.allowParallelSyncs
2323 || activeOp.account.name.equals(candidate.account.name))) {
Fred Quintana918339a2010-10-05 14:00:39 -07002324 conflict = activeSyncContext;
2325 // don't break out since we want to do a full count of the varieties
2326 } else {
2327 if (candidateIsInitialization == activeOp.isInitialization()
2328 && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
2329 longRunning = activeSyncContext;
2330 // don't break out since we want to do a full count of the varieties
2331 }
2332 }
2333 }
2334
2335 if (isLoggable) {
2336 Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
2337 Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
2338 Log.v(TAG, " longRunning: " + longRunning);
2339 Log.v(TAG, " conflict: " + conflict);
Fred Quintanadc475562012-05-04 15:51:54 -07002340 Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002341 }
2342
Fred Quintanadc475562012-05-04 15:51:54 -07002343 final boolean roomAvailable = candidateIsInitialization
2344 ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
2345 : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
2346
Fred Quintana918339a2010-10-05 14:00:39 -07002347 if (conflict != null) {
2348 if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
2349 && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
2350 toReschedule = conflict;
2351 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2352 Log.v(TAG, "canceling and rescheduling sync since an initialization "
2353 + "takes higher priority, " + conflict);
2354 }
2355 } else if (candidate.expedited && !conflict.mSyncOperation.expedited
2356 && (candidateIsInitialization
2357 == conflict.mSyncOperation.isInitialization())) {
2358 toReschedule = conflict;
2359 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2360 Log.v(TAG, "canceling and rescheduling sync since an expedited "
2361 + "takes higher priority, " + conflict);
2362 }
2363 } else {
2364 continue;
2365 }
Fred Quintanadc475562012-05-04 15:51:54 -07002366 } else if (roomAvailable) {
2367 // dispatch candidate
2368 } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null
2369 && !candidateIsInitialization) {
2370 // We found an active, non-expedited regular sync. We also know that the
2371 // candidate doesn't conflict with this active sync since conflict
2372 // is null. Reschedule the active sync and start the candidate.
2373 toReschedule = oldestNonExpeditedRegular;
2374 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2375 Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, "
2376 + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002377 }
Fred Quintanadc475562012-05-04 15:51:54 -07002378 } else if (longRunning != null
2379 && (candidateIsInitialization
2380 == longRunning.mSyncOperation.isInitialization())) {
2381 // We found an active, long-running sync. Reschedule the active
2382 // sync and start the candidate.
2383 toReschedule = longRunning;
2384 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2385 Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
2386 + longRunning);
2387 }
2388 } else {
2389 // we were unable to find or make space to run this candidate, go on to
2390 // the next one
2391 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002392 }
2393
2394 if (toReschedule != null) {
2395 runSyncFinishedOrCanceledLocked(null, toReschedule);
2396 scheduleSyncOperation(toReschedule.mSyncOperation);
2397 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002398 synchronized (mSyncQueue) {
Fred Quintana918339a2010-10-05 14:00:39 -07002399 mSyncQueue.remove(candidate);
2400 }
Fred Quintana87b14662011-09-12 10:32:55 -07002401 dispatchSyncOperation(candidate);
Fred Quintana918339a2010-10-05 14:00:39 -07002402 }
2403
2404 return nextReadyToRunTime;
2405 }
2406
Fred Quintana87b14662011-09-12 10:32:55 -07002407 private boolean dispatchSyncOperation(SyncOperation op) {
Fred Quintana918339a2010-10-05 14:00:39 -07002408 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002409 Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
Fred Quintana918339a2010-10-05 14:00:39 -07002410 Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
2411 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
2412 Log.v(TAG, syncContext.toString());
2413 }
2414 }
2415
Fred Quintana718d8a22009-04-29 17:53:20 -07002416 // connect to the sync adapter
Fred Quintana4a6679b2009-08-17 13:05:39 -07002417 SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002418 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2419 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId);
Fred Quintana718d8a22009-04-29 17:53:20 -07002420 if (syncAdapterInfo == null) {
Fred Quintana7620f1a2010-03-16 15:58:44 -07002421 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
2422 + ", removing settings for it");
Amith Yamasani04e0d262012-02-14 11:50:53 -08002423 mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority);
Fred Quintana87b14662011-09-12 10:32:55 -07002424 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002425 }
2426
Fred Quintana718d8a22009-04-29 17:53:20 -07002427 ActiveSyncContext activeSyncContext =
Fred Quintana918339a2010-10-05 14:00:39 -07002428 new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
2429 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
2430 mActiveSyncContexts.add(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002431 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002432 Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002433 }
Dianne Hackborn41203752012-08-31 14:05:51 -07002434 if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
Fred Quintana718d8a22009-04-29 17:53:20 -07002435 Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
Fred Quintana918339a2010-10-05 14:00:39 -07002436 closeActiveSyncContext(activeSyncContext);
Fred Quintana87b14662011-09-12 10:32:55 -07002437 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -07002438 }
2439
Fred Quintana87b14662011-09-12 10:32:55 -07002440 return true;
Fred Quintana718d8a22009-04-29 17:53:20 -07002441 }
2442
Alon Alberteca75112010-12-08 15:02:33 -08002443 private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext,
Fred Quintana918339a2010-10-05 14:00:39 -07002444 ISyncAdapter syncAdapter) {
2445 activeSyncContext.mSyncAdapter = syncAdapter;
2446 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
Fred Quintana718d8a22009-04-29 17:53:20 -07002447 try {
Alon Alberteca75112010-12-08 15:02:33 -08002448 activeSyncContext.mIsLinkedToDeath = true;
2449 syncAdapter.asBinder().linkToDeath(activeSyncContext, 0);
2450
Fred Quintana918339a2010-10-05 14:00:39 -07002451 syncAdapter.startSync(activeSyncContext, syncOperation.authority,
Fred Quintana21bb0de2009-06-16 10:24:58 -07002452 syncOperation.account, syncOperation.extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002453 } catch (RemoteException remoteExc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002454 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
2455 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002456 increaseBackoffSetting(syncOperation);
2457 scheduleSyncOperation(new SyncOperation(syncOperation));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002458 } catch (RuntimeException exc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002459 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002460 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 }
2463
Amith Yamasani04e0d262012-02-14 11:50:53 -08002464 private void cancelActiveSyncLocked(Account account, int userId, String authority) {
Fred Quintana918339a2010-10-05 14:00:39 -07002465 ArrayList<ActiveSyncContext> activeSyncs =
2466 new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
2467 for (ActiveSyncContext activeSyncContext : activeSyncs) {
2468 if (activeSyncContext != null) {
Alon Albert8e285552012-09-17 15:05:27 -07002469 // if an account was specified then only cancel the sync if it matches
Fred Quintana918339a2010-10-05 14:00:39 -07002470 if (account != null) {
2471 if (!account.equals(activeSyncContext.mSyncOperation.account)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002472 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002473 }
2474 }
Alon Albert8e285552012-09-17 15:05:27 -07002475 // if an authority was specified then only cancel the sync if it matches
Fred Quintana918339a2010-10-05 14:00:39 -07002476 if (authority != null) {
2477 if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002478 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002479 }
2480 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002481 // check if the userid matches
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002482 if (userId != UserHandle.USER_ALL
Amith Yamasani04e0d262012-02-14 11:50:53 -08002483 && userId != activeSyncContext.mSyncOperation.userId) {
2484 continue;
2485 }
Fred Quintana918339a2010-10-05 14:00:39 -07002486 runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
2487 activeSyncContext);
2488 }
2489 }
2490 }
2491
2492 private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
2493 ActiveSyncContext activeSyncContext) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002494 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Alon Alberteca75112010-12-08 15:02:33 -08002495
2496 if (activeSyncContext.mIsLinkedToDeath) {
2497 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
2498 activeSyncContext.mIsLinkedToDeath = false;
2499 }
Fred Quintana918339a2010-10-05 14:00:39 -07002500 closeActiveSyncContext(activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002501
2502 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
2503
2504 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
2505
2506 String historyMessage;
2507 int downstreamActivity;
2508 int upstreamActivity;
2509 if (syncResult != null) {
2510 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002511 Log.v(TAG, "runSyncFinishedOrCanceled [finished]: "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002512 + syncOperation + ", result " + syncResult);
2513 }
2514
2515 if (!syncResult.hasError()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002516 historyMessage = SyncStorageEngine.MESG_SUCCESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002517 // TODO: set these correctly when the SyncResult is extended to include it
2518 downstreamActivity = 0;
2519 upstreamActivity = 0;
Alon Albert6e079a32010-11-12 12:41:09 -08002520 clearBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002521 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -08002522 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
2523 // the operation failed so increase the backoff time
2524 if (!syncResult.syncAlreadyInProgress) {
2525 increaseBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002526 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002527 // reschedule the sync if so indicated by the syncResult
2528 maybeRescheduleSync(syncResult, syncOperation);
Alon Albert57286f92012-10-09 14:21:38 -07002529 historyMessage = ContentResolver.syncErrorToString(
2530 syncResultToErrorNumber(syncResult));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 // TODO: set these correctly when the SyncResult is extended to include it
2532 downstreamActivity = 0;
2533 upstreamActivity = 0;
2534 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002535
2536 setDelayUntilTime(syncOperation, syncResult.delayUntil);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002537 } else {
2538 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002539 Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002540 }
Fred Quintana718d8a22009-04-29 17:53:20 -07002541 if (activeSyncContext.mSyncAdapter != null) {
2542 try {
Fred Quintana21bb0de2009-06-16 10:24:58 -07002543 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002544 } catch (RemoteException e) {
2545 // we don't need to retry this in this case
2546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002547 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002548 historyMessage = SyncStorageEngine.MESG_CANCELED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002549 downstreamActivity = 0;
2550 upstreamActivity = 0;
2551 }
2552
2553 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
2554 upstreamActivity, downstreamActivity, elapsedTime);
2555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 if (syncResult != null && syncResult.tooManyDeletions) {
2557 installHandleTooManyDeletesNotification(syncOperation.account,
Dianne Hackborn41203752012-08-31 14:05:51 -07002558 syncOperation.authority, syncResult.stats.numDeletes,
2559 syncOperation.userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002560 } else {
Dianne Hackborn41203752012-08-31 14:05:51 -07002561 mNotificationMgr.cancelAsUser(null,
2562 syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(),
2563 new UserHandle(syncOperation.userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002564 }
2565
2566 if (syncResult != null && syncResult.fullSyncRequested) {
Matthew Williamsfa774182013-06-18 15:44:11 -07002567 scheduleSyncOperation(
2568 new SyncOperation(syncOperation.account, syncOperation.userId,
2569 syncOperation.reason,
2570 syncOperation.syncSource, syncOperation.authority, new Bundle(),
2571 0 /* delay */, 0 /* flex */,
2572 syncOperation.backoff, syncOperation.delayUntil,
2573 syncOperation.allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002574 }
2575 // no need to schedule an alarm, as that will be done by our caller.
2576 }
2577
Fred Quintana918339a2010-10-05 14:00:39 -07002578 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
2579 activeSyncContext.close();
2580 mActiveSyncContexts.remove(activeSyncContext);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002581 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
2582 activeSyncContext.mSyncOperation.userId);
Fred Quintana918339a2010-10-05 14:00:39 -07002583 }
2584
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002585 /**
2586 * Convert the error-containing SyncResult into the Sync.History error number. Since
2587 * the SyncResult may indicate multiple errors at once, this method just returns the
2588 * most "serious" error.
2589 * @param syncResult the SyncResult from which to read
2590 * @return the most "serious" error set in the SyncResult
2591 * @throws IllegalStateException if the SyncResult does not indicate any errors.
2592 * If SyncResult.error() is true then it is safe to call this.
2593 */
2594 private int syncResultToErrorNumber(SyncResult syncResult) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002595 if (syncResult.syncAlreadyInProgress)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002596 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002597 if (syncResult.stats.numAuthExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002598 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002599 if (syncResult.stats.numIoExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002600 return ContentResolver.SYNC_ERROR_IO;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002601 if (syncResult.stats.numParseExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002602 return ContentResolver.SYNC_ERROR_PARSE;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002603 if (syncResult.stats.numConflictDetectedExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002604 return ContentResolver.SYNC_ERROR_CONFLICT;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002605 if (syncResult.tooManyDeletions)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002606 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002607 if (syncResult.tooManyRetries)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002608 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002609 if (syncResult.databaseError)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002610 return ContentResolver.SYNC_ERROR_INTERNAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002611 throw new IllegalStateException("we are not in an error state, " + syncResult);
2612 }
2613
Fred Quintana918339a2010-10-05 14:00:39 -07002614 private void manageSyncNotificationLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002615 boolean shouldCancel;
2616 boolean shouldInstall;
2617
Fred Quintana918339a2010-10-05 14:00:39 -07002618 if (mActiveSyncContexts.isEmpty()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 mSyncNotificationInfo.startTime = null;
2620
2621 // we aren't syncing. if the notification is active then remember that we need
2622 // to cancel it and then clear out the info
2623 shouldCancel = mSyncNotificationInfo.isActive;
2624 shouldInstall = false;
2625 } else {
2626 // we are syncing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002627 final long now = SystemClock.elapsedRealtime();
2628 if (mSyncNotificationInfo.startTime == null) {
2629 mSyncNotificationInfo.startTime = now;
2630 }
2631
Fred Quintana918339a2010-10-05 14:00:39 -07002632 // there are three cases:
2633 // - the notification is up: do nothing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002634 // - the notification is not up but it isn't time yet: don't install
2635 // - the notification is not up and it is time: need to install
2636
2637 if (mSyncNotificationInfo.isActive) {
Fred Quintana918339a2010-10-05 14:00:39 -07002638 shouldInstall = shouldCancel = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002639 } else {
Fred Quintana918339a2010-10-05 14:00:39 -07002640 // it isn't currently up, so there is nothing to cancel
2641 shouldCancel = false;
2642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002643 final boolean timeToShowNotification =
2644 now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
Fred Quintana918339a2010-10-05 14:00:39 -07002645 if (timeToShowNotification) {
2646 shouldInstall = true;
2647 } else {
2648 // show the notification immediately if this is a manual sync
2649 shouldInstall = false;
2650 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2651 final boolean manualSync = activeSyncContext.mSyncOperation.extras
2652 .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
2653 if (manualSync) {
2654 shouldInstall = true;
2655 break;
2656 }
2657 }
2658 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002659 }
2660 }
2661
2662 if (shouldCancel && !shouldInstall) {
2663 mNeedSyncActiveNotification = false;
2664 sendSyncStateIntent();
2665 mSyncNotificationInfo.isActive = false;
2666 }
2667
2668 if (shouldInstall) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 mNeedSyncActiveNotification = true;
2670 sendSyncStateIntent();
2671 mSyncNotificationInfo.isActive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672 }
2673 }
2674
Fred Quintana918339a2010-10-05 14:00:39 -07002675 private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
2676 long nextPendingEventElapsedTime) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002677 // in each of these cases the sync loop will be kicked, which will cause this
2678 // method to be called again
2679 if (!mDataConnectionIsConnected) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002680 if (mStorageIsLow) return;
2681
Fred Quintana918339a2010-10-05 14:00:39 -07002682 // When the status bar notification should be raised
2683 final long notificationTime =
2684 (!mSyncHandler.mSyncNotificationInfo.isActive
2685 && mSyncHandler.mSyncNotificationInfo.startTime != null)
2686 ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY
2687 : Long.MAX_VALUE;
Fred Quintana307da1a2010-01-21 14:24:20 -08002688
Fred Quintana918339a2010-10-05 14:00:39 -07002689 // When we should consider canceling an active sync
2690 long earliestTimeoutTime = Long.MAX_VALUE;
2691 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
2692 final long currentSyncTimeoutTime =
2693 currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
2694 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2695 Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
2696 + currentSyncTimeoutTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002697 }
Fred Quintana918339a2010-10-05 14:00:39 -07002698 if (earliestTimeoutTime > currentSyncTimeoutTime) {
2699 earliestTimeoutTime = currentSyncTimeoutTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002700 }
2701 }
2702
Fred Quintana918339a2010-10-05 14:00:39 -07002703 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2704 Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime);
2705 }
2706
2707 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2708 Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
2709 }
2710
2711 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2712 Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
2713 + nextPeriodicEventElapsedTime);
2714 }
2715 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2716 Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
2717 + nextPendingEventElapsedTime);
2718 }
2719
2720 long alarmTime = Math.min(notificationTime, earliestTimeoutTime);
2721 alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime);
2722 alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
2723
2724 // Bound the alarm time.
2725 final long now = SystemClock.elapsedRealtime();
2726 if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
2727 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2728 Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
2729 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002730 }
Fred Quintana918339a2010-10-05 14:00:39 -07002731 alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
2732 } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) {
2733 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2734 Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, "
2735 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
2736 }
2737 alarmTime = now + SYNC_ALARM_TIMEOUT_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002738 }
2739
2740 // determine if we need to set or cancel the alarm
2741 boolean shouldSet = false;
2742 boolean shouldCancel = false;
Matthew Williamsba352712013-08-13 15:53:31 -07002743 final boolean alarmIsActive = (mAlarmScheduleTime != null) && (now < mAlarmScheduleTime);
Fred Quintana918339a2010-10-05 14:00:39 -07002744 final boolean needAlarm = alarmTime != Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002745 if (needAlarm) {
Matthew Williamsba352712013-08-13 15:53:31 -07002746 // Need the alarm if
2747 // - it's currently not set
2748 // - if the alarm is set in the past.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) {
2750 shouldSet = true;
2751 }
2752 } else {
2753 shouldCancel = alarmIsActive;
2754 }
2755
Matthew Williamsfa774182013-06-18 15:44:11 -07002756 // Set or cancel the alarm as directed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002757 ensureAlarmService();
2758 if (shouldSet) {
Fred Quintana918339a2010-10-05 14:00:39 -07002759 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2760 Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
2761 + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
2762 + " secs from now");
2763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002764 mAlarmScheduleTime = alarmTime;
Matthew Williamsba352712013-08-13 15:53:31 -07002765 mAlarmService.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002766 mSyncAlarmIntent);
2767 } else if (shouldCancel) {
2768 mAlarmScheduleTime = null;
2769 mAlarmService.cancel(mSyncAlarmIntent);
2770 }
2771 }
2772
2773 private void sendSyncStateIntent() {
2774 Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07002775 syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002776 syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
Fred Quintana918339a2010-10-05 14:00:39 -07002777 syncStateIntent.putExtra("failing", false);
Amith Yamasanicd757062012-10-19 18:23:52 -07002778 mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002779 }
2780
Fred Quintanad9d2f112009-04-23 13:36:27 -07002781 private void installHandleTooManyDeletesNotification(Account account, String authority,
Dianne Hackborn41203752012-08-31 14:05:51 -07002782 long numDeletes, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002783 if (mNotificationMgr == null) return;
Fred Quintanac848b702009-08-25 20:18:46 -07002784
2785 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
2786 authority, 0 /* flags */);
2787 if (providerInfo == null) {
2788 return;
2789 }
2790 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
2791
Fred Quintanab19e62a2010-12-16 13:54:43 -08002792 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002793 clickIntent.putExtra("account", account);
Tadashi G. Takaoka86135d32009-09-24 19:31:44 -07002794 clickIntent.putExtra("authority", authority);
Fred Quintanac848b702009-08-25 20:18:46 -07002795 clickIntent.putExtra("provider", authorityName.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002796 clickIntent.putExtra("numDeletes", numDeletes);
2797
2798 if (!isActivityAvailable(clickIntent)) {
2799 Log.w(TAG, "No activity found to handle too many deletes.");
2800 return;
2801 }
2802
2803 final PendingIntent pendingIntent = PendingIntent
Dianne Hackborn41203752012-08-31 14:05:51 -07002804 .getActivityAsUser(mContext, 0, clickIntent,
2805 PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002806
2807 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
2808 R.string.contentServiceTooManyDeletesNotificationDesc);
2809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002810 Notification notification =
2811 new Notification(R.drawable.stat_notify_sync_error,
2812 mContext.getString(R.string.contentServiceSync),
2813 System.currentTimeMillis());
2814 notification.setLatestEventInfo(mContext,
2815 mContext.getString(R.string.contentServiceSyncNotificationTitle),
Fred Quintanac848b702009-08-25 20:18:46 -07002816 String.format(tooManyDeletesDescFormat.toString(), authorityName),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 pendingIntent);
2818 notification.flags |= Notification.FLAG_ONGOING_EVENT;
Dianne Hackborn41203752012-08-31 14:05:51 -07002819 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(),
2820 notification, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002821 }
2822
2823 /**
2824 * Checks whether an activity exists on the system image for the given intent.
2825 *
2826 * @param intent The intent for an activity.
2827 * @return Whether or not an activity exists.
2828 */
2829 private boolean isActivityAvailable(Intent intent) {
2830 PackageManager pm = mContext.getPackageManager();
2831 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
2832 int listSize = list.size();
2833 for (int i = 0; i < listSize; i++) {
2834 ResolveInfo resolveInfo = list.get(i);
2835 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
2836 != 0) {
2837 return true;
2838 }
2839 }
2840
2841 return false;
2842 }
2843
2844 public long insertStartSyncEvent(SyncOperation syncOperation) {
2845 final int source = syncOperation.syncSource;
2846 final long now = System.currentTimeMillis();
2847
Dianne Hackborn231cc602009-04-27 17:10:36 -07002848 EventLog.writeEvent(2720, syncOperation.authority,
Doug Zongker44f57472009-09-20 15:52:43 -07002849 SyncStorageEngine.EVENT_START, source,
2850 syncOperation.account.name.hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002851
2852 return mSyncStorageEngine.insertStartSyncEvent(
Alon Albert57286f92012-10-09 14:21:38 -07002853 syncOperation.account, syncOperation.userId, syncOperation.reason,
2854 syncOperation.authority,
2855 now, source, syncOperation.isInitialization(), syncOperation.extras
2856 );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002857 }
2858
2859 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
2860 int upstreamActivity, int downstreamActivity, long elapsedTime) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002861 EventLog.writeEvent(2720, syncOperation.authority,
Doug Zongker44f57472009-09-20 15:52:43 -07002862 SyncStorageEngine.EVENT_STOP, syncOperation.syncSource,
2863 syncOperation.account.name.hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002864
Fred Quintana77c560f2010-03-29 22:20:26 -07002865 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002866 resultMessage, downstreamActivity, upstreamActivity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002867 }
2868 }
Fred Quintana918339a2010-10-05 14:00:39 -07002869
2870 private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
2871 for (ActiveSyncContext sync : mActiveSyncContexts) {
2872 if (sync == activeSyncContext) {
2873 return true;
2874 }
2875 }
2876 return false;
2877 }
Alon Albert57286f92012-10-09 14:21:38 -07002878
2879 static class PrintTable {
2880 private ArrayList<Object[]> mTable = Lists.newArrayList();
2881 private final int mCols;
2882
2883 PrintTable(int cols) {
2884 mCols = cols;
2885 }
2886
2887 void set(int row, int col, Object... values) {
2888 if (col + values.length > mCols) {
2889 throw new IndexOutOfBoundsException("Table only has " + mCols +
2890 " columns. can't set " + values.length + " at column " + col);
2891 }
2892 for (int i = mTable.size(); i <= row; i++) {
2893 final Object[] list = new Object[mCols];
2894 mTable.add(list);
2895 for (int j = 0; j < mCols; j++) {
2896 list[j] = "";
2897 }
2898 }
2899 System.arraycopy(values, 0, mTable.get(row), col, values.length);
2900 }
2901
2902 void writeTo(PrintWriter out) {
2903 final String[] formats = new String[mCols];
2904 int totalLength = 0;
2905 for (int col = 0; col < mCols; ++col) {
2906 int maxLength = 0;
2907 for (Object[] row : mTable) {
2908 final int length = row[col].toString().length();
2909 if (length > maxLength) {
2910 maxLength = length;
2911 }
2912 }
2913 totalLength += maxLength;
2914 formats[col] = String.format("%%-%ds", maxLength);
2915 }
2916 printRow(out, formats, mTable.get(0));
2917 totalLength += (mCols - 1) * 2;
2918 for (int i = 0; i < totalLength; ++i) {
2919 out.print("-");
2920 }
2921 out.println();
2922 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
2923 Object[] row = mTable.get(i);
2924 printRow(out, formats, row);
2925 }
2926 }
2927
2928 private void printRow(PrintWriter out, String[] formats, Object[] row) {
2929 for (int j = 0, rowLength = row.length; j < rowLength; j++) {
2930 out.printf(String.format(formats[j], row[j].toString()));
2931 out.print(" ");
2932 }
2933 out.println();
2934 }
2935
2936 public int getNumRows() {
2937 return mTable.size();
2938 }
2939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002940}