blob: 0185a21645c1daf4914fa230f1d29653caddecdb [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;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070034import android.content.ISyncServiceAdapter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080035import android.content.ISyncStatusObserver;
36import android.content.Intent;
37import android.content.IntentFilter;
Matthew Williamsfa774182013-06-18 15:44:11 -070038import android.content.PeriodicSync;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080039import android.content.ServiceConnection;
40import android.content.SyncActivityTooManyDeletes;
41import android.content.SyncAdapterType;
42import android.content.SyncAdaptersCache;
43import android.content.SyncInfo;
44import android.content.SyncResult;
45import android.content.SyncStatusInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070046import android.content.pm.ApplicationInfo;
Amith Yamasani9422bdc2013-04-10 16:58:19 -070047import android.content.pm.PackageInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070048import android.content.pm.PackageManager;
49import android.content.pm.ProviderInfo;
50import android.content.pm.RegisteredServicesCache;
51import android.content.pm.RegisteredServicesCacheListener;
52import android.content.pm.ResolveInfo;
Amith Yamasani04e0d262012-02-14 11:50:53 -080053import android.content.pm.UserInfo;
Alon Alberte0bde332011-09-22 14:26:16 -070054import android.net.ConnectivityManager;
55import android.net.NetworkInfo;
Fred Quintana918339a2010-10-05 14:00:39 -070056import android.os.Bundle;
57import android.os.Handler;
Fred Quintana918339a2010-10-05 14:00:39 -070058import android.os.IBinder;
59import android.os.Looper;
60import android.os.Message;
61import android.os.PowerManager;
Fred Quintana918339a2010-10-05 14:00:39 -070062import android.os.RemoteException;
63import android.os.SystemClock;
64import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070065import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070066import android.os.UserManager;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070067import android.os.WorkSource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.text.format.DateUtils;
70import android.text.format.Time;
Matthew Williams632515b2013-10-10 15:51:00 -070071import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.util.EventLog;
73import android.util.Log;
Fred Quintana307da1a2010-01-21 14:24:20 -080074import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
Alon Albert8e285552012-09-17 15:05:27 -070076import com.android.internal.R;
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -080077import com.android.internal.annotations.GuardedBy;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070078import com.android.internal.os.BackgroundThread;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070079import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080080import com.android.server.accounts.AccountManagerService;
Georgi Nikolovdbe846b2013-06-25 14:09:56 -070081import com.android.server.content.SyncStorageEngine.AuthorityInfo;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080082import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
Alon Albert8e285552012-09-17 15:05:27 -070083import com.google.android.collect.Lists;
84import com.google.android.collect.Maps;
85import com.google.android.collect.Sets;
86
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088import java.io.PrintWriter;
89import java.util.ArrayList;
Alon Alberte0bde332011-09-22 14:26:16 -070090import java.util.Arrays;
Fred Quintana918339a2010-10-05 14:00:39 -070091import java.util.Collection;
92import java.util.Collections;
Alon Alberte0bde332011-09-22 14:26:16 -070093import java.util.Comparator;
Fred Quintanab3029c32010-04-06 13:27:12 -070094import java.util.HashMap;
Dianne Hackborn231cc602009-04-27 17:10:36 -070095import java.util.HashSet;
Fred Quintana918339a2010-10-05 14:00:39 -070096import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097import java.util.List;
Alon Alberte0bde332011-09-22 14:26:16 -070098import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099import java.util.Random;
Alon Albert8e285552012-09-17 15:05:27 -0700100import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
102/**
103 * @hide
104 */
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700105public class SyncManager {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private static final String TAG = "SyncManager";
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 /** Delay a sync due to local changes this long. In milliseconds */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700109 private static final long LOCAL_SYNC_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 /**
112 * If a sync takes longer than this and the sync queue is not empty then we will
113 * cancel it and add it back to the end of the sync queue. In milliseconds.
114 */
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700115 private static final long MAX_TIME_PER_SYNC;
116
117 static {
Dianne Hackbornb4e12492013-06-26 15:08:19 -0700118 final boolean isLargeRAM = !ActivityManager.isLowRamDeviceStatic();
Fred Quintana33e44692011-12-05 15:04:16 -0800119 int defaultMaxInitSyncs = isLargeRAM ? 5 : 2;
120 int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1;
121 MAX_SIMULTANEOUS_INITIALIZATION_SYNCS =
122 SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs);
123 MAX_SIMULTANEOUS_REGULAR_SYNCS =
124 SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs);
Fred Quintana918339a2010-10-05 14:00:39 -0700125 LOCAL_SYNC_DELAY =
126 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
127 MAX_TIME_PER_SYNC =
128 SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
129 SYNC_NOTIFICATION_DELAY =
130 SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
Debajit Ghosh44ee0f02009-09-14 14:58:31 -0700131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
Fred Quintana918339a2010-10-05 14:00:39 -0700133 private static final long SYNC_NOTIFICATION_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 /**
136 * When retrying a sync for the first time use this delay. After that
137 * the retry time will double until it reached MAX_SYNC_RETRY_TIME.
138 * In milliseconds.
139 */
140 private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds
141
142 /**
143 * Default the max sync retry time to this value.
144 */
145 private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour
146
147 /**
Fred Quintana8570f742010-02-18 10:32:54 -0800148 * How long to wait before retrying a sync that failed due to one already being in progress.
149 */
150 private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
151
Fred Quintana3ec47302010-03-10 10:08:31 -0800152 private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
153
Dianne Hackborne746f032010-09-13 16:02:57 -0700154 private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700155 private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
Fred Quintana918339a2010-10-05 14:00:39 -0700156 private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
157
158 private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
159 private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
161 private Context mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
Amith Yamasani04e0d262012-02-14 11:50:53 -0800163 private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
164
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700165 // TODO: add better locking around mRunningAccounts
166 private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
Fred Quintana918339a2010-10-05 14:00:39 -0700169 volatile private PowerManager.WakeLock mSyncManagerWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 volatile private boolean mDataConnectionIsConnected = false;
171 volatile private boolean mStorageIsLow = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172
173 private final NotificationManager mNotificationMgr;
174 private AlarmManager mAlarmService = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700176 private SyncStorageEngine mSyncStorageEngine;
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700177
Jeff Sharkey8b2c3a142012-11-12 11:45:05 -0800178 @GuardedBy("mSyncQueue")
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700179 private final SyncQueue mSyncQueue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700181 protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 // set if the sync active indicator should be reported
184 private boolean mNeedSyncActiveNotification = false;
185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 private final PendingIntent mSyncAlarmIntent;
Fred Quintanaf892fb32009-08-27 21:32:08 -0700187 // Synchronized on "this". Instead of using this directly one should instead call
188 // its accessor, getConnManager().
189 private ConnectivityManager mConnManagerDoNotUseDirectly;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700191 protected SyncAdaptersCache mSyncAdapters;
Fred Quintana718d8a22009-04-29 17:53:20 -0700192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 private BroadcastReceiver mStorageIntentReceiver =
194 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700195 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 String action = intent.getAction();
198 if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
199 if (Log.isLoggable(TAG, Log.VERBOSE)) {
200 Log.v(TAG, "Internal storage is low.");
201 }
202 mStorageIsLow = true;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700203 cancelActiveSync(
204 SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
205 null /* any sync */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
207 if (Log.isLoggable(TAG, Log.VERBOSE)) {
208 Log.v(TAG, "Internal storage is ok.");
209 }
210 mStorageIsLow = false;
211 sendCheckAlarmsMessage();
212 }
213 }
214 };
215
Fred Quintana60307342009-03-24 22:48:12 -0700216 private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700217 @Override
Fred Quintana60307342009-03-24 22:48:12 -0700218 public void onReceive(Context context, Intent intent) {
Fred Quintanae91ebe22009-09-29 20:44:30 -0700219 mSyncHandler.onBootCompleted();
Fred Quintana60307342009-03-24 22:48:12 -0700220 }
221 };
222
Amith Yamasanid648a602012-09-26 15:06:10 -0700223 private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700224 @Override
Amith Yamasanid648a602012-09-26 15:06:10 -0700225 public void onReceive(Context context, Intent intent) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700226 updateRunningAccounts();
227
228 // Kick off sync for everyone, since this was a radical account change
Alon Albert57286f92012-10-09 14:21:38 -0700229 scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null,
Matthew Williamsfa774182013-06-18 15:44:11 -0700230 null, 0 /* no delay */, 0/* no delay */, false);
Amith Yamasanid648a602012-09-26 15:06:10 -0700231 }
232 };
233
Fred Quintanab3029c32010-04-06 13:27:12 -0700234 private final PowerManager mPowerManager;
235
Matthew Williamsfa774182013-06-18 15:44:11 -0700236 // Use this as a random offset to seed all periodic syncs.
Ashish Sharma69d95de2012-04-11 17:27:24 -0700237 private int mSyncRandomOffsetMillis;
238
Amith Yamasani9535c912012-10-10 21:48:33 -0700239 private final UserManager mUserManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700240
Fred Quintana918339a2010-10-05 14:00:39 -0700241 private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
242 private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
243
Amith Yamasani258848d2012-08-10 17:06:33 -0700244 private List<UserInfo> getAllUsers() {
Amith Yamasani9535c912012-10-10 21:48:33 -0700245 return mUserManager.getUsers();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800246 }
247
248 private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
249 boolean found = false;
250 for (int i = 0; i < accounts.length; i++) {
251 if (accounts[i].userId == userId
252 && accounts[i].account.equals(account)) {
253 found = true;
254 break;
255 }
256 }
257 return found;
258 }
259
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700260 public void updateRunningAccounts() {
261 mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800262
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700263 if (mBootCompleted) {
264 doDatabaseCleanup();
265 }
266
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700267 AccountAndUser[] accounts = mRunningAccounts;
Fred Quintana918339a2010-10-05 14:00:39 -0700268 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700269 if (!containsAccountAndUser(accounts,
270 currentSyncContext.mSyncOperation.target.account,
271 currentSyncContext.mSyncOperation.target.userId)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700272 Log.d(TAG, "canceling sync since the account is no longer running");
Fred Quintana918339a2010-10-05 14:00:39 -0700273 sendSyncFinishedOrCanceledMessage(currentSyncContext,
Fred Quintanad9d2f112009-04-23 13:36:27 -0700274 null /* no result since this is a cancel */);
275 }
276 }
Fred Quintanad9d2f112009-04-23 13:36:27 -0700277 // we must do this since we don't bother scheduling alarms when
278 // the accounts are not set yet
279 sendCheckAlarmsMessage();
Fred Quintanad9d2f112009-04-23 13:36:27 -0700280 }
281
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700282 private void doDatabaseCleanup() {
Amith Yamasanidb6a14c2012-10-17 21:16:52 -0700283 for (UserInfo user : mUserManager.getUsers(true)) {
284 // Skip any partially created/removed users
285 if (user.partial) continue;
Jeff Sharkey8f55d112012-10-11 11:00:21 -0700286 Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
287 mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
288 }
289 }
290
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 private BroadcastReceiver mConnectivityIntentReceiver =
292 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700293 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 public void onReceive(Context context, Intent intent) {
Alon Alberted1d2532011-02-15 14:02:14 -0800295 final boolean wasConnected = mDataConnectionIsConnected;
296
297 // don't use the intent to figure out if network is connected, just check
298 // ConnectivityManager directly.
Alon Albert1bad83a2011-02-16 10:29:56 -0800299 mDataConnectionIsConnected = readDataConnectionState();
Alon Alberted1d2532011-02-15 14:02:14 -0800300 if (mDataConnectionIsConnected) {
301 if (!wasConnected) {
302 if (Log.isLoggable(TAG, Log.VERBOSE)) {
303 Log.v(TAG, "Reconnection detected: clearing all backoffs");
304 }
305 mSyncStorageEngine.clearAllBackoffs(mSyncQueue);
306 }
307 sendCheckAlarmsMessage();
308 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 }
310 };
311
Alon Albert1bad83a2011-02-16 10:29:56 -0800312 private boolean readDataConnectionState() {
Alon Alberted1d2532011-02-15 14:02:14 -0800313 NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
314 return (networkInfo != null) && networkInfo.isConnected();
315 }
316
Dianne Hackborn55280a92009-05-07 15:53:46 -0700317 private BroadcastReceiver mShutdownIntentReceiver =
318 new BroadcastReceiver() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700319 @Override
Dianne Hackborn55280a92009-05-07 15:53:46 -0700320 public void onReceive(Context context, Intent intent) {
321 Log.w(TAG, "Writing sync state before shutdown...");
322 getSyncStorageEngine().writeAllState();
323 }
324 };
325
Amith Yamasani13593602012-03-22 16:16:17 -0700326 private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
327 @Override
328 public void onReceive(Context context, Intent intent) {
Alon Albert8e285552012-09-17 15:05:27 -0700329 String action = intent.getAction();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700330 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
331 if (userId == UserHandle.USER_NULL) return;
332
Alon Albert8e285552012-09-17 15:05:27 -0700333 if (Intent.ACTION_USER_REMOVED.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700334 onUserRemoved(userId);
335 } else if (Intent.ACTION_USER_STARTING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700336 onUserStarting(userId);
337 } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700338 onUserStopping(userId);
Alon Albert8e285552012-09-17 15:05:27 -0700339 }
Amith Yamasani13593602012-03-22 16:16:17 -0700340 }
341 };
342
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 private final SyncHandler mSyncHandler;
345
Fred Quintana4f9cfc52009-09-02 15:20:23 -0700346 private volatile boolean mBootCompleted = false;
347
Fred Quintanaf892fb32009-08-27 21:32:08 -0700348 private ConnectivityManager getConnectivityManager() {
349 synchronized (this) {
350 if (mConnManagerDoNotUseDirectly == null) {
351 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
352 Context.CONNECTIVITY_SERVICE);
353 }
354 return mConnManagerDoNotUseDirectly;
355 }
356 }
357
Jeff Sharkeye4996bb2012-10-17 14:16:28 -0700358 /**
359 * Should only be created after {@link ContentService#systemReady()} so that
360 * {@link PackageManager} is ready to query.
361 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 public SyncManager(Context context, boolean factoryTest) {
363 // Initialize the SyncStorageEngine first, before registering observers
364 // and creating threads and so on; it may fail if the disk is full.
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700365 mContext = context;
Amith Yamasani9535c912012-10-10 21:48:33 -0700366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 SyncStorageEngine.init(context);
368 mSyncStorageEngine = SyncStorageEngine.getSingleton();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800369 mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700370 @Override
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700371 public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
372 if (info.target_provider) {
373 scheduleSync(info.account, info.userId, reason, info.provider, extras,
374 0 /* no flex */,
375 0 /* run immediately */,
376 false);
377 } else if (info.target_service) {
378 scheduleSync(info.service, info.userId, reason, extras,
379 0 /* no flex */,
380 0 /* run immediately */);
381 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800382 }
383 });
384
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700385 mSyncAdapters = new SyncAdaptersCache(mContext);
Alon Albert57286f92012-10-09 14:21:38 -0700386 mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700388 mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
Fred Quintana44037e62010-01-21 13:14:49 -0800390 mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700391 @Override
392 public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
Fred Quintana44037e62010-01-21 13:14:49 -0800393 if (!removed) {
Alon Albert57286f92012-10-09 14:21:38 -0700394 scheduleSync(null, UserHandle.USER_ALL,
395 SyncOperation.REASON_SERVICE_CHANGED,
Matthew Williamsfa774182013-06-18 15:44:11 -0700396 type.authority, null, 0 /* no delay */, 0 /* no delay */,
Fred Quintana44037e62010-01-21 13:14:49 -0800397 false /* onlyThoseWithUnkownSyncableState */);
398 }
399 }
400 }, mSyncHandler);
Fred Quintana718d8a22009-04-29 17:53:20 -0700401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 mSyncAlarmIntent = PendingIntent.getBroadcast(
403 mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
406 context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
407
Fred Quintanae91ebe22009-09-29 20:44:30 -0700408 if (!factoryTest) {
409 intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
410 context.registerReceiver(mBootCompletedReceiver, intentFilter);
411 }
Fred Quintana60307342009-03-24 22:48:12 -0700412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
414 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
415 context.registerReceiver(mStorageIntentReceiver, intentFilter);
416
Dianne Hackborn55280a92009-05-07 15:53:46 -0700417 intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
418 intentFilter.setPriority(100);
419 context.registerReceiver(mShutdownIntentReceiver, intentFilter);
420
Amith Yamasani13593602012-03-22 16:16:17 -0700421 intentFilter = new IntentFilter();
422 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700423 intentFilter.addAction(Intent.ACTION_USER_STARTING);
424 intentFilter.addAction(Intent.ACTION_USER_STOPPING);
Alon Albert8e285552012-09-17 15:05:27 -0700425 mContext.registerReceiverAsUser(
426 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
Amith Yamasani13593602012-03-22 16:16:17 -0700427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 if (!factoryTest) {
429 mNotificationMgr = (NotificationManager)
430 context.getSystemService(Context.NOTIFICATION_SERVICE);
431 context.registerReceiver(new SyncAlarmIntentReceiver(),
432 new IntentFilter(ACTION_SYNC_ALARM));
433 } else {
434 mNotificationMgr = null;
435 }
Fred Quintanab3029c32010-04-06 13:27:12 -0700436 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
Amith Yamasani9535c912012-10-10 21:48:33 -0700437 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438
439 // This WakeLock is used to ensure that we stay awake between the time that we receive
440 // a sync alarm notification and when we finish processing it. We need to do this
441 // because we don't do the work in the alarm handler, rather we do it in a message
442 // handler.
Fred Quintanab3029c32010-04-06 13:27:12 -0700443 mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 HANDLE_SYNC_ALARM_WAKE_LOCK);
445 mHandleAlarmWakeLock.setReferenceCounted(false);
446
Fred Quintana918339a2010-10-05 14:00:39 -0700447 // This WakeLock is used to ensure that we stay awake while running the sync loop
448 // message handler. Normally we will hold a sync adapter wake lock while it is being
449 // synced but during the execution of the sync loop it might finish a sync for
450 // one sync adapter before starting the sync for the other sync adapter and we
451 // don't want the device to go to sleep during that window.
452 mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
453 SYNC_LOOP_WAKE_LOCK);
454 mSyncManagerWakeLock.setReferenceCounted(false);
455
Dianne Hackborn231cc602009-04-27 17:10:36 -0700456 mSyncStorageEngine.addStatusChangeListener(
Fred Quintanaac9385e2009-06-22 18:00:59 -0700457 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700458 @Override
Dianne Hackborn231cc602009-04-27 17:10:36 -0700459 public void onStatusChanged(int which) {
460 // force the sync loop to run if the settings change
461 sendCheckAlarmsMessage();
462 }
463 });
Fred Quintanae91ebe22009-09-29 20:44:30 -0700464
465 if (!factoryTest) {
Amith Yamasanid648a602012-09-26 15:06:10 -0700466 // Register for account list updates for all users
467 mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
468 UserHandle.ALL,
469 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
470 null, null);
Fred Quintanae91ebe22009-09-29 20:44:30 -0700471 }
Ashish Sharma69d95de2012-04-11 17:27:24 -0700472
473 // Pick a random second in a day to seed all periodic syncs
474 mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 }
476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 /**
478 * Return a random value v that satisfies minValue <= v < maxValue. The difference between
479 * maxValue and minValue must be less than Integer.MAX_VALUE.
480 */
481 private long jitterize(long minValue, long maxValue) {
482 Random random = new Random(SystemClock.elapsedRealtime());
483 long spread = maxValue - minValue;
484 if (spread > Integer.MAX_VALUE) {
485 throw new IllegalArgumentException("the difference between the maxValue and the "
486 + "minValue must be less than " + Integer.MAX_VALUE);
487 }
488 return minValue + random.nextInt((int)spread);
489 }
490
Dianne Hackborn231cc602009-04-27 17:10:36 -0700491 public SyncStorageEngine getSyncStorageEngine() {
492 return mSyncStorageEngine;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493 }
Doug Zongker44f57472009-09-20 15:52:43 -0700494
Amith Yamasani9422bdc2013-04-10 16:58:19 -0700495 public int getIsSyncable(Account account, int userId, String providerName) {
496 int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
497 UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
498
499 // If it's not a restricted user, return isSyncable
500 if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
501
502 // Else check if the sync adapter has opted-in or not
503 RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
504 mSyncAdapters.getServiceInfo(
505 SyncAdapterType.newKey(providerName, account.type), userId);
506 if (syncAdapterInfo == null) return isSyncable;
507
508 PackageInfo pInfo = null;
509 try {
510 pInfo = AppGlobals.getPackageManager().getPackageInfo(
511 syncAdapterInfo.componentName.getPackageName(), 0, userId);
512 if (pInfo == null) return isSyncable;
513 } catch (RemoteException re) {
514 // Shouldn't happen
515 return isSyncable;
516 }
517 if (pInfo.restrictedAccountType != null
518 && pInfo.restrictedAccountType.equals(account.type)) {
519 return isSyncable;
520 } else {
521 return 0;
522 }
523 }
524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 private void ensureAlarmService() {
526 if (mAlarmService == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700527 mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 }
529 }
530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 /**
Matthew Williamsfa774182013-06-18 15:44:11 -0700532 * Initiate a sync using the new anonymous service API.
Matthew Williamsfa774182013-06-18 15:44:11 -0700533 * @param cname SyncService component bound to in order to perform the sync.
Amith Yamasani04e0d262012-02-14 11:50:53 -0800534 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
535 * then all users' accounts are considered.
Matthew Williamsfa774182013-06-18 15:44:11 -0700536 * @param uid Linux uid of the application that is performing the sync.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 * @param extras a Map of SyncAdapter-specific information to control
Matthew Williams8ef22042013-07-26 12:56:39 -0700538 * syncs of a specific provider. Cannot be null.
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700539 * @param beforeRunTimeMillis milliseconds before <code>runtimeMillis</code> that this sync may
540 * be run.
541 * @param runtimeMillis milliseconds from now by which this sync must be run.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 */
Matthew Williamsfa774182013-06-18 15:44:11 -0700543 public void scheduleSync(ComponentName cname, int userId, int uid, Bundle extras,
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700544 long beforeRunTimeMillis, long runtimeMillis) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Matthew Williamsfa774182013-06-18 15:44:11 -0700546 if (isLoggable) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700547 Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
Matthew Williamsfa774182013-06-18 15:44:11 -0700548 }
Matthew Williams06485a72013-07-26 12:56:39 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
551 if (expedited) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700552 runtimeMillis = -1; // this means schedule at the front of the queue
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 }
554
Fred Quintana53bd2522010-02-05 15:28:12 -0800555 final boolean ignoreSettings =
556 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700557 int source = SyncStorageEngine.SOURCE_SERVICE;
Matthew Williams7a2ab3a2013-09-11 14:25:51 -0700558 boolean isEnabled = mSyncStorageEngine.getIsTargetServiceActive(cname, userId);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700559 // Only schedule this sync if
560 // - we've explicitly been told to ignore settings.
561 // - global sync is enabled for this user.
562 boolean syncAllowed =
563 ignoreSettings
564 || mSyncStorageEngine.getMasterSyncAutomatically(userId);
565 if (!syncAllowed) {
566 if (isLoggable) {
567 Log.d(TAG, "scheduleSync: sync of " + cname + " not allowed, dropping request.");
568 }
569 return;
570 }
Matthew Williams8ef22042013-07-26 12:56:39 -0700571 if (!isEnabled) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700572 if (isLoggable) {
573 Log.d(TAG, "scheduleSync: " + cname + " is not enabled, dropping request");
574 }
575 return;
576 }
577 SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId);
578 Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
579 long delayUntil = mSyncStorageEngine.getDelayUntilTime(info);
580 final long backoffTime = backoff != null ? backoff.first : 0;
Matthew Williams8ef22042013-07-26 12:56:39 -0700581 if (isLoggable) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700582 Log.v(TAG, "schedule Sync:"
583 + ", delay until " + delayUntil
584 + ", run by " + runtimeMillis
585 + ", flex " + beforeRunTimeMillis
586 + ", source " + source
587 + ", sync service " + cname
588 + ", extras " + extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
Matthew Williams8ef22042013-07-26 12:56:39 -0700590 scheduleSyncOperation(
591 new SyncOperation(cname, userId, uid, source, extras,
592 runtimeMillis /* runtime */,
593 beforeRunTimeMillis /* flextime */,
594 backoffTime,
595 delayUntil));
Matthew Williamsfa774182013-06-18 15:44:11 -0700596 }
597
598 /**
599 * Initiate a sync. This can start a sync for all providers
600 * (pass null to url, set onlyTicklable to false), only those
601 * providers that are marked as ticklable (pass null to url,
602 * set onlyTicklable to true), or a specific provider (set url
603 * to the content url of the provider).
604 *
605 * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
606 * true then initiate a sync that just checks for local changes to send
607 * to the server, otherwise initiate a sync that first gets any
608 * changes from the server before sending local changes back to
609 * the server.
610 *
611 * <p>If a specific provider is being synced (the url is non-null)
612 * then the extras can contain SyncAdapter-specific information
613 * to control what gets synced (e.g. which specific feed to sync).
614 *
615 * <p>You'll start getting callbacks after this.
616 *
617 * @param requestedAccount the account to sync, may be null to signify all accounts
618 * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
619 * then all users' accounts are considered.
620 * @param reason for sync request. If this is a positive integer, it is the Linux uid
621 * assigned to the process that requested the sync. If it's negative, the sync was requested by
622 * the SyncManager itself and could be one of the following:
623 * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
624 * {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
625 * {@link SyncOperation#REASON_SERVICE_CHANGED}
626 * {@link SyncOperation#REASON_PERIODIC}
627 * {@link SyncOperation#REASON_IS_SYNCABLE}
628 * {@link SyncOperation#REASON_SYNC_AUTO}
629 * {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
630 * {@link SyncOperation#REASON_USER_START}
631 * @param requestedAuthority the authority to sync, may be null to indicate all authorities
632 * @param extras a Map of SyncAdapter-specific information to control
633 * syncs of a specific provider. Can be null. Is ignored
634 * if the url is null.
635 * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run.
636 * @param runtimeMillis maximum milliseconds in the future to wait before performing sync.
637 * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
638 */
639 public void scheduleSync(Account requestedAccount, int userId, int reason,
640 String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
641 long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
642 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
643
Matthew Williamsfa774182013-06-18 15:44:11 -0700644 if (extras == null) {
645 extras = new Bundle();
646 }
647 if (isLoggable) {
648 Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " "
649 + requestedAuthority);
650 }
651 Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
652 if (expedited) {
653 runtimeMillis = -1; // this means schedule at the front of the queue
654 }
655
656 AccountAndUser[] accounts;
657 if (requestedAccount != null && userId != UserHandle.USER_ALL) {
658 accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
659 } else {
Matthew Williamsfa774182013-06-18 15:44:11 -0700660 accounts = mRunningAccounts;
661 if (accounts.length == 0) {
662 if (isLoggable) {
663 Log.v(TAG, "scheduleSync: no accounts configured, dropping");
664 }
665 return;
666 }
667 }
668
669 final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
670 final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
671 if (manualSync) {
672 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
673 extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
674 }
675 final boolean ignoreSettings =
676 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
677
678 int source;
679 if (uploadOnly) {
680 source = SyncStorageEngine.SOURCE_LOCAL;
681 } else if (manualSync) {
682 source = SyncStorageEngine.SOURCE_USER;
683 } else if (requestedAuthority == null) {
684 source = SyncStorageEngine.SOURCE_POLL;
685 } else {
686 // this isn't strictly server, since arbitrary callers can (and do) request
687 // a non-forced two-way sync on a specific url
688 source = SyncStorageEngine.SOURCE_SERVER;
689 }
690
691 for (AccountAndUser account : accounts) {
692 // Compile a list of authorities that have sync adapters.
693 // For each authority sync each account that matches a sync adapter.
694 final HashSet<String> syncableAuthorities = new HashSet<String>();
695 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
696 mSyncAdapters.getAllServices(account.userId)) {
697 syncableAuthorities.add(syncAdapter.type.authority);
698 }
699
700 // if the url was specified then replace the list of authorities
701 // with just this authority or clear it if this authority isn't
702 // syncable
703 if (requestedAuthority != null) {
704 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
705 syncableAuthorities.clear();
706 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
707 }
708
709 for (String authority : syncableAuthorities) {
710 int isSyncable = getIsSyncable(account.account, account.userId,
711 authority);
712 if (isSyncable == 0) {
713 continue;
714 }
715 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
716 syncAdapterInfo = mSyncAdapters.getServiceInfo(
717 SyncAdapterType.newKey(authority, account.account.type), account.userId);
718 if (syncAdapterInfo == null) {
719 continue;
720 }
721 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
722 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
723 if (isSyncable < 0 && isAlwaysSyncable) {
724 mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
725 isSyncable = 1;
726 }
727 if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
728 continue;
729 }
730 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
731 continue;
732 }
733
Matthew Williamsfa774182013-06-18 15:44:11 -0700734 boolean syncAllowed =
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700735 (isSyncable < 0) // always allow if the isSyncable state is unknown
Matthew Williamsfa774182013-06-18 15:44:11 -0700736 || ignoreSettings
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700737 || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
Matthew Williamsfa774182013-06-18 15:44:11 -0700738 && mSyncStorageEngine.getSyncAutomatically(account.account,
739 account.userId, authority));
740 if (!syncAllowed) {
741 if (isLoggable) {
742 Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
743 + " is not allowed, dropping request");
744 }
745 continue;
746 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700747 SyncStorageEngine.EndPoint info =
748 new SyncStorageEngine.EndPoint(
749 account.account, authority, account.userId);
750 Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
751 long delayUntil =
752 mSyncStorageEngine.getDelayUntilTime(info);
Matthew Williamsfa774182013-06-18 15:44:11 -0700753 final long backoffTime = backoff != null ? backoff.first : 0;
754 if (isSyncable < 0) {
755 // Initialisation sync.
756 Bundle newExtras = new Bundle();
757 newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
758 if (isLoggable) {
759 Log.v(TAG, "schedule initialisation Sync:"
760 + ", delay until " + delayUntil
761 + ", run by " + 0
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700762 + ", flex " + 0
Matthew Williamsfa774182013-06-18 15:44:11 -0700763 + ", source " + source
764 + ", account " + account
765 + ", authority " + authority
766 + ", extras " + newExtras);
767 }
768 scheduleSyncOperation(
769 new SyncOperation(account.account, account.userId, reason, source,
770 authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
771 backoffTime, delayUntil, allowParallelSyncs));
772 }
773 if (!onlyThoseWithUnkownSyncableState) {
774 if (isLoggable) {
775 Log.v(TAG, "scheduleSync:"
776 + " delay until " + delayUntil
777 + " run by " + runtimeMillis
778 + " flex " + beforeRuntimeMillis
779 + ", source " + source
780 + ", account " + account
781 + ", authority " + authority
782 + ", extras " + extras);
783 }
784 scheduleSyncOperation(
785 new SyncOperation(account.account, account.userId, reason, source,
786 authority, extras, runtimeMillis, beforeRuntimeMillis,
787 backoffTime, delayUntil, allowParallelSyncs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789 }
790 }
791 }
792
Matthew Williamsfa774182013-06-18 15:44:11 -0700793 /**
794 * Schedule sync based on local changes to a provider. Occurs within interval
795 * [LOCAL_SYNC_DELAY, 2*LOCAL_SYNC_DELAY].
796 */
Alon Albert57286f92012-10-09 14:21:38 -0700797 public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 final Bundle extras = new Bundle();
799 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
Matthew Williamsfa774182013-06-18 15:44:11 -0700800 scheduleSync(account, userId, reason, authority, extras,
801 LOCAL_SYNC_DELAY /* earliest run time */,
802 2 * LOCAL_SYNC_DELAY /* latest sync time. */,
Fred Quintana4a6679b2009-08-17 13:05:39 -0700803 false /* onlyThoseWithUnkownSyncableState */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700806 public SyncAdapterType[] getSyncAdapterTypes(int userId) {
807 final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
808 serviceInfos = mSyncAdapters.getAllServices(userId);
Fred Quintanaac9385e2009-06-22 18:00:59 -0700809 SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
810 int i = 0;
811 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
812 types[i] = serviceInfo.type;
813 ++i;
814 }
815 return types;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 }
817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 private void sendSyncAlarmMessage() {
819 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM");
820 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM);
821 }
822
823 private void sendCheckAlarmsMessage() {
824 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
Ashish Sharma69d95de2012-04-11 17:27:24 -0700825 mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
827 }
828
829 private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
830 SyncResult syncResult) {
831 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED");
832 Message msg = mSyncHandler.obtainMessage();
833 msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
834 msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult);
835 mSyncHandler.sendMessage(msg);
836 }
837
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700838 private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) {
Fred Quintana918339a2010-10-05 14:00:39 -0700839 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
840 Message msg = mSyncHandler.obtainMessage();
841 msg.what = SyncHandler.MESSAGE_CANCEL;
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700842 msg.setData(extras);
843 msg.obj = info;
Fred Quintana918339a2010-10-05 14:00:39 -0700844 mSyncHandler.sendMessage(msg);
845 }
846
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 class SyncHandlerMessagePayload {
848 public final ActiveSyncContext activeSyncContext;
849 public final SyncResult syncResult;
850
851 SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) {
852 this.activeSyncContext = syncContext;
853 this.syncResult = syncResult;
854 }
855 }
856
857 class SyncAlarmIntentReceiver extends BroadcastReceiver {
Matthew Williamsfa774182013-06-18 15:44:11 -0700858 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 public void onReceive(Context context, Intent intent) {
860 mHandleAlarmWakeLock.acquire();
861 sendSyncAlarmMessage();
862 }
863 }
864
Alon Albert6e079a32010-11-12 12:41:09 -0800865 private void clearBackoffSetting(SyncOperation op) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700866 mSyncStorageEngine.setBackoff(op.target,
867 SyncStorageEngine.NOT_IN_BACKOFF_MODE,
868 SyncStorageEngine.NOT_IN_BACKOFF_MODE);
Alon Albert6e079a32010-11-12 12:41:09 -0800869 synchronized (mSyncQueue) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700870 mSyncQueue.onBackoffChanged(op.target, 0);
Alon Albert6e079a32010-11-12 12:41:09 -0800871 }
872 }
873
Fred Quintana307da1a2010-01-21 14:24:20 -0800874 private void increaseBackoffSetting(SyncOperation op) {
Ashish Sharma69d95de2012-04-11 17:27:24 -0700875 // TODO: Use this function to align it to an already scheduled sync
876 // operation in the specified window
Fred Quintana307da1a2010-01-21 14:24:20 -0800877 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878
Fred Quintana307da1a2010-01-21 14:24:20 -0800879 final Pair<Long, Long> previousSettings =
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700880 mSyncStorageEngine.getBackoff(op.target);
Alon Albertaeeb6202010-12-09 16:14:02 -0800881 long newDelayInMs = -1;
882 if (previousSettings != null) {
883 // don't increase backoff before current backoff is expired. This will happen for op's
884 // with ignoreBackoff set.
885 if (now < previousSettings.first) {
886 if (Log.isLoggable(TAG, Log.VERBOSE)) {
887 Log.v(TAG, "Still in backoff, do not increase it. "
888 + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
889 }
890 return;
891 }
892 // Subsequent delays are the double of the previous delay
893 newDelayInMs = previousSettings.second * 2;
894 }
895 if (newDelayInMs <= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS
897 newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
898 (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
900
901 // Cap the delay
Jeff Sharkey625239a2012-09-26 22:03:49 -0700902 long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(),
903 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
905 if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
906 newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
907 }
908
Alon Albertc1ac7762010-10-28 13:35:55 -0700909 final long backoff = now + newDelayInMs;
910
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700911 mSyncStorageEngine.setBackoff(op.target, backoff, newDelayInMs);
Alon Albertc1ac7762010-10-28 13:35:55 -0700912 op.backoff = backoff;
913 op.updateEffectiveRunTime();
914
Fred Quintana918339a2010-10-05 14:00:39 -0700915 synchronized (mSyncQueue) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700916 mSyncQueue.onBackoffChanged(op.target, backoff);
Fred Quintana918339a2010-10-05 14:00:39 -0700917 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800918 }
919
920 private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
921 final long delayUntil = delayUntilSeconds * 1000;
922 final long absoluteNow = System.currentTimeMillis();
923 long newDelayUntilTime;
924 if (delayUntil > absoluteNow) {
925 newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow);
926 } else {
927 newDelayUntilTime = 0;
928 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700929 mSyncStorageEngine.setDelayUntilTime(op.target, newDelayUntilTime);
Fred Quintana918339a2010-10-05 14:00:39 -0700930 synchronized (mSyncQueue) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700931 mSyncQueue.onDelayUntilTimeChanged(op.target, newDelayUntilTime);
Fred Quintana918339a2010-10-05 14:00:39 -0700932 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 }
934
935 /**
Matthew Williams8ef22042013-07-26 12:56:39 -0700936 * Cancel the active sync if it matches the target.
937 * @param info object containing info about which syncs to cancel. The target can
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700938 * have null account/provider info to specify all accounts/providers.
939 * @param extras if non-null, specifies the exact sync to remove.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 */
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700941 public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) {
942 sendCancelSyncsMessage(info, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 }
944
945 /**
946 * Create and schedule a SyncOperation.
947 *
948 * @param syncOperation the SyncOperation to schedule
949 */
950 public void scheduleSyncOperation(SyncOperation syncOperation) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800951 boolean queueChanged;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 synchronized (mSyncQueue) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800953 queueChanged = mSyncQueue.add(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 }
955
Fred Quintana307da1a2010-01-21 14:24:20 -0800956 if (queueChanged) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 if (Log.isLoggable(TAG, Log.VERBOSE)) {
958 Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
959 }
960 sendCheckAlarmsMessage();
961 } else {
962 if (Log.isLoggable(TAG, Log.VERBOSE)) {
963 Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
964 + syncOperation);
965 }
966 }
967 }
968
969 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -0700970 * Remove scheduled sync operations.
Matthew Williams8ef22042013-07-26 12:56:39 -0700971 * @param info limit the removals to operations that match this target. The target can
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700972 * have null account/provider info to specify all accounts/providers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 */
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700974 public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 synchronized (mSyncQueue) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700976 mSyncQueue.remove(info, null /* all operations */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700978 mSyncStorageEngine.setBackoff(info,
Fred Quintana918339a2010-10-05 14:00:39 -0700979 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 }
981
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700982 /**
983 * Remove a specified sync, if it exists.
984 * @param info Authority for which the sync is to be removed.
985 * @param extras extras bundle to uniquely identify sync.
986 */
987 public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
988 synchronized (mSyncQueue) {
989 mSyncQueue.remove(info, extras);
990 }
991 // Reset the back-off if there are no more syncs pending.
992 if (!mSyncStorageEngine.isSyncPending(info)) {
993 mSyncStorageEngine.setBackoff(info,
994 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
995 }
996 }
997
Fred Quintana307da1a2010-01-21 14:24:20 -0800998 void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
1000 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08001001 Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001004 operation = new SyncOperation(operation, 0L /* newRunTimeFromNow */);
Fred Quintana53bd2522010-02-05 15:28:12 -08001005
1006 // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
1007 // request. Retries of the request will always honor the backoff, so clear the
1008 // flag in case we retry this request.
1009 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
1010 operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
1011 }
1012
Fred Quintana53bd2522010-02-05 15:28:12 -08001013 if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001014 if (isLoggable) {
1015 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
1016 + operation);
1017 }
Fred Quintana918339a2010-10-05 14:00:39 -07001018 } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
1019 && !syncResult.syncAlreadyInProgress) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001020 // If this was an upward sync then schedule a two-way sync immediately.
Fred Quintana53bd2522010-02-05 15:28:12 -08001021 operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001022 if (isLoggable) {
1023 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
1024 + "encountered an error: " + operation);
1025 }
Fred Quintana53bd2522010-02-05 15:28:12 -08001026 scheduleSyncOperation(operation);
Fred Quintana307da1a2010-01-21 14:24:20 -08001027 } else if (syncResult.tooManyRetries) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001028 // If this sync aborted because the internal sync loop retried too many times then
1029 // don't reschedule. Otherwise we risk getting into a retry loop.
1030 if (isLoggable) {
1031 Log.d(TAG, "not retrying sync operation because it retried too many times: "
1032 + operation);
1033 }
Fred Quintanaaa7edda2009-12-03 14:18:58 -08001034 } else if (syncResult.madeSomeProgress()) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001035 // If the operation succeeded to some extent then retry immediately.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08001037 Log.d(TAG, "retrying sync operation because even though it had an error "
1038 + "it achieved some success");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
Fred Quintana53bd2522010-02-05 15:28:12 -08001040 scheduleSyncOperation(operation);
Fred Quintana8570f742010-02-18 10:32:54 -08001041 } else if (syncResult.syncAlreadyInProgress) {
1042 if (isLoggable) {
1043 Log.d(TAG, "retrying sync operation that failed because there was already a "
1044 + "sync in progress: " + operation);
1045 }
Matthew Williamsfa774182013-06-18 15:44:11 -07001046 scheduleSyncOperation(
1047 new SyncOperation(
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001048 operation,
1049 DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000 /* newRunTimeFromNow */)
1050 );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 } else if (syncResult.hasSoftError()) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001052 // If this was a two-way sync then retry soft errors with an exponential backoff.
Fred Quintana307da1a2010-01-21 14:24:20 -08001053 if (isLoggable) {
1054 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
1055 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
Fred Quintana53bd2522010-02-05 15:28:12 -08001057 scheduleSyncOperation(operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 } else {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001059 // Otherwise do not reschedule.
Fred Quintana307da1a2010-01-21 14:24:20 -08001060 Log.d(TAG, "not retrying sync operation because the error is a hard error: "
1061 + operation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 }
1063 }
1064
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001065 private void onUserStarting(int userId) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001066 // Make sure that accounts we're about to use are valid
1067 AccountManagerService.getSingleton().validateAccounts(userId);
1068
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001069 mSyncAdapters.invalidateCache(userId);
1070
1071 updateRunningAccounts();
1072
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07001073 synchronized (mSyncQueue) {
1074 mSyncQueue.addPendingOperations(userId);
1075 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001076
1077 // Schedule sync for any accounts under started user
Jeff Sharkey8f55d112012-10-11 11:00:21 -07001078 final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001079 for (Account account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -07001080 scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
Matthew Williamsfa774182013-06-18 15:44:11 -07001081 0 /* no delay */, 0 /* No flex */,
1082 true /* onlyThoseWithUnknownSyncableState */);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001083 }
1084
1085 sendCheckAlarmsMessage();
Alon Albert8e285552012-09-17 15:05:27 -07001086 }
Amith Yamasani13593602012-03-22 16:16:17 -07001087
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001088 private void onUserStopping(int userId) {
1089 updateRunningAccounts();
1090
1091 cancelActiveSync(
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001092 new SyncStorageEngine.EndPoint(
1093 null /* any account */,
1094 null /* any authority */,
1095 userId),
1096 null /* any sync. */
1097 );
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001098 }
1099
1100 private void onUserRemoved(int userId) {
1101 updateRunningAccounts();
1102
Amith Yamasani13593602012-03-22 16:16:17 -07001103 // Clean up the storage engine database
1104 mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001105 synchronized (mSyncQueue) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001106 mSyncQueue.removeUserLocked(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001107 }
1108 }
1109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 /**
1111 * @hide
1112 */
Alon Alberteca75112010-12-08 15:02:33 -08001113 class ActiveSyncContext extends ISyncContext.Stub
1114 implements ServiceConnection, IBinder.DeathRecipient {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 final SyncOperation mSyncOperation;
1116 final long mHistoryRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001117 ISyncAdapter mSyncAdapter;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001118 ISyncServiceAdapter mSyncServiceAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 final long mStartTime;
1120 long mTimeoutStartTime;
Fred Quintana3ec47302010-03-10 10:08:31 -08001121 boolean mBound;
Fred Quintana918339a2010-10-05 14:00:39 -07001122 final PowerManager.WakeLock mSyncWakeLock;
1123 final int mSyncAdapterUid;
1124 SyncInfo mSyncInfo;
Alon Alberteca75112010-12-08 15:02:33 -08001125 boolean mIsLinkedToDeath = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126
Fred Quintana918339a2010-10-05 14:00:39 -07001127 /**
1128 * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
1129 * sync adapter. Since this grabs the wakelock you need to be sure to call
1130 * close() when you are done with this ActiveSyncContext, whether the sync succeeded
1131 * or not.
1132 * @param syncOperation the SyncOperation we are about to sync
1133 * @param historyRowId the row in which to record the history info for this sync
1134 * @param syncAdapterUid the UID of the application that contains the sync adapter
1135 * for this sync. This is used to attribute the wakelock hold to that application.
1136 */
1137 public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
1138 int syncAdapterUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 super();
Fred Quintana918339a2010-10-05 14:00:39 -07001140 mSyncAdapterUid = syncAdapterUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 mSyncOperation = syncOperation;
1142 mHistoryRowId = historyRowId;
Fred Quintana718d8a22009-04-29 17:53:20 -07001143 mSyncAdapter = null;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001144 mSyncServiceAdapter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 mStartTime = SystemClock.elapsedRealtime();
1146 mTimeoutStartTime = mStartTime;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001147 mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation);
Fred Quintana918339a2010-10-05 14:00:39 -07001148 mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
1149 mSyncWakeLock.acquire();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
1151
1152 public void sendHeartbeat() {
Fred Quintana307da1a2010-01-21 14:24:20 -08001153 // heartbeats are no longer used
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 }
1155
1156 public void onFinished(SyncResult result) {
Fred Quintana918339a2010-10-05 14:00:39 -07001157 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 // include "this" in the message so that the handler can ignore it if this
1159 // ActiveSyncContext is no longer the mActiveSyncContext at message handling
1160 // time
1161 sendSyncFinishedOrCanceledMessage(this, result);
1162 }
1163
1164 public void toString(StringBuilder sb) {
1165 sb.append("startTime ").append(mStartTime)
1166 .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
1167 .append(", mHistoryRowId ").append(mHistoryRowId)
1168 .append(", syncOperation ").append(mSyncOperation);
1169 }
1170
Fred Quintana718d8a22009-04-29 17:53:20 -07001171 public void onServiceConnected(ComponentName name, IBinder service) {
1172 Message msg = mSyncHandler.obtainMessage();
1173 msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001174 msg.obj = new ServiceConnectionData(this, service);
Fred Quintana718d8a22009-04-29 17:53:20 -07001175 mSyncHandler.sendMessage(msg);
1176 }
1177
1178 public void onServiceDisconnected(ComponentName name) {
1179 Message msg = mSyncHandler.obtainMessage();
1180 msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
1181 msg.obj = new ServiceConnectionData(this, null);
1182 mSyncHandler.sendMessage(msg);
1183 }
1184
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001185 boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001186 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001187 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
Fred Quintana718d8a22009-04-29 17:53:20 -07001188 }
1189 Intent intent = new Intent();
1190 intent.setAction("android.content.SyncAdapter");
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001191 intent.setComponent(serviceComponent);
Dianne Hackborndd9b82c2009-09-03 00:18:47 -07001192 intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1193 com.android.internal.R.string.sync_binding_label);
Dianne Hackborn41203752012-08-31 14:05:51 -07001194 intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1195 mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
1196 null, new UserHandle(userId)));
Fred Quintana3ec47302010-03-10 10:08:31 -08001197 mBound = true;
Amith Yamasani27b89e62013-01-16 12:30:11 -08001198 final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Dianne Hackborne02c88a2011-10-28 13:58:15 -07001199 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
Amith Yamasani04e0d262012-02-14 11:50:53 -08001200 | Context.BIND_ALLOW_OOM_MANAGEMENT,
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001201 new UserHandle(mSyncOperation.target.userId));
Fred Quintana3ec47302010-03-10 10:08:31 -08001202 if (!bindResult) {
1203 mBound = false;
1204 }
1205 return bindResult;
Fred Quintana718d8a22009-04-29 17:53:20 -07001206 }
1207
Fred Quintana918339a2010-10-05 14:00:39 -07001208 /**
1209 * Performs the required cleanup, which is the releasing of the wakelock and
1210 * unbinding from the sync adapter (if actually bound).
1211 */
Fred Quintana3ec47302010-03-10 10:08:31 -08001212 protected void close() {
Fred Quintana718d8a22009-04-29 17:53:20 -07001213 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1214 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
1215 }
Fred Quintana3ec47302010-03-10 10:08:31 -08001216 if (mBound) {
1217 mBound = false;
1218 mContext.unbindService(this);
1219 }
Fred Quintana918339a2010-10-05 14:00:39 -07001220 mSyncWakeLock.release();
Dianne Hackbornc24ab862011-10-18 15:55:03 -07001221 mSyncWakeLock.setWorkSource(null);
Fred Quintana718d8a22009-04-29 17:53:20 -07001222 }
1223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 public String toString() {
1225 StringBuilder sb = new StringBuilder();
1226 toString(sb);
1227 return sb.toString();
1228 }
Alon Alberteca75112010-12-08 15:02:33 -08001229
1230 @Override
1231 public void binderDied() {
1232 sendSyncFinishedOrCanceledMessage(this, null);
1233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 }
1235
1236 protected void dump(FileDescriptor fd, PrintWriter pw) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001237 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
1238 dumpSyncState(ipw);
1239 dumpSyncHistory(ipw);
1240 dumpSyncAdapters(ipw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 }
1242
Dianne Hackborn231cc602009-04-27 17:10:36 -07001243 static String formatTime(long time) {
1244 Time tobj = new Time();
1245 tobj.set(time);
1246 return tobj.format("%Y-%m-%d %H:%M:%S");
1247 }
Doug Zongker44f57472009-09-20 15:52:43 -07001248
Alon Alberte0bde332011-09-22 14:26:16 -07001249 protected void dumpSyncState(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001250 pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001251 pw.print("auto sync: ");
1252 List<UserInfo> users = getAllUsers();
1253 if (users != null) {
1254 for (UserInfo user : users) {
1255 pw.print("u" + user.id + "="
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001256 + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001257 }
1258 pw.println();
1259 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07001260 pw.print("memory low: "); pw.println(mStorageIsLow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001261
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001262 final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001263
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001264 pw.print("accounts: ");
Fred Quintana53bd2522010-02-05 15:28:12 -08001265 if (accounts != INITIAL_ACCOUNTS_ARRAY) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001266 pw.println(accounts.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 } else {
Fred Quintana53bd2522010-02-05 15:28:12 -08001268 pw.println("not known yet");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 final long now = SystemClock.elapsedRealtime();
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001271 pw.print("now: "); pw.print(now);
1272 pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
Ashish Sharma69d95de2012-04-11 17:27:24 -07001273 pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000));
1274 pw.println(" (HH:MM:SS)");
Dianne Hackborn231cc602009-04-27 17:10:36 -07001275 pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
1276 pw.println(" (HH:MM:SS)");
1277 pw.print("time spent syncing: ");
1278 pw.print(DateUtils.formatElapsedTime(
1279 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
1280 pw.print(" (HH:MM:SS), sync ");
1281 pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
1282 pw.println("in progress");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 if (mSyncHandler.mAlarmScheduleTime != null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001284 pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
1285 pw.print(" (");
1286 pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
1287 pw.println(" (HH:MM:SS) from now)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 } else {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001289 pw.println("no alarm is scheduled (there had better not be any pending syncs)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 }
1291
Dianne Hackborn231cc602009-04-27 17:10:36 -07001292 pw.print("notification info: ");
Alon Alberte0bde332011-09-22 14:26:16 -07001293 final StringBuilder sb = new StringBuilder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 mSyncHandler.mSyncNotificationInfo.toString(sb);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001295 pw.println(sb.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296
Fred Quintana918339a2010-10-05 14:00:39 -07001297 pw.println();
1298 pw.println("Active Syncs: " + mActiveSyncContexts.size());
Alon Albert57286f92012-10-09 14:21:38 -07001299 final PackageManager pm = mContext.getPackageManager();
Fred Quintana918339a2010-10-05 14:00:39 -07001300 for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
1301 final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
1302 pw.print(" ");
1303 pw.print(DateUtils.formatElapsedTime(durationInSeconds));
1304 pw.print(" - ");
Alon Albert57286f92012-10-09 14:21:38 -07001305 pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
Fred Quintana918339a2010-10-05 14:00:39 -07001306 pw.println();
1307 }
1308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 synchronized (mSyncQueue) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001310 sb.setLength(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 mSyncQueue.dump(sb);
Matthew Williamsfa774182013-06-18 15:44:11 -07001312 // Dump Pending Operations.
1313 getSyncStorageEngine().dumpPendingOperations(sb);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 }
Matthew Williamsfa774182013-06-18 15:44:11 -07001315
Fred Quintana918339a2010-10-05 14:00:39 -07001316 pw.println();
1317 pw.print(sb.toString());
Dianne Hackborn231cc602009-04-27 17:10:36 -07001318
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001319 // join the installed sync adapter with the accounts list and emit for everything
1320 pw.println();
1321 pw.println("Sync Status");
Amith Yamasani04e0d262012-02-14 11:50:53 -08001322 for (AccountAndUser account : accounts) {
Alon Albert57286f92012-10-09 14:21:38 -07001323 pw.printf("Account %s u%d %s\n",
1324 account.account.name, account.userId, account.account.type);
1325
1326 pw.println("=======================================================================");
1327 final PrintTable table = new PrintTable(13);
1328 table.set(0, 0,
1329 "Authority", // 0
1330 "Syncable", // 1
1331 "Enabled", // 2
1332 "Delay", // 3
1333 "Loc", // 4
1334 "Poll", // 5
1335 "Per", // 6
1336 "Serv", // 7
1337 "User", // 8
1338 "Tot", // 9
1339 "Time", // 10
1340 "Last Sync", // 11
1341 "Periodic" // 12
1342 );
1343
1344 final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
1345 Lists.newArrayList();
1346 sorted.addAll(mSyncAdapters.getAllServices(account.userId));
1347 Collections.sort(sorted,
1348 new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
1349 @Override
1350 public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
1351 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
1352 return lhs.type.authority.compareTo(rhs.type.authority);
1353 }
1354 });
1355 for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001356 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001357 continue;
1358 }
Alon Albert57286f92012-10-09 14:21:38 -07001359 int row = table.getNumRows();
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07001360 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
1361 mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001362 new SyncStorageEngine.EndPoint(
1363 account.account,
1364 syncAdapterType.type.authority,
1365 account.userId));
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07001366 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
1367 SyncStatusInfo status = syncAuthoritySyncStatus.second;
Matthew Williams8ef22042013-07-26 12:56:39 -07001368 String authority = settings.target.provider;
Alon Albert57286f92012-10-09 14:21:38 -07001369 if (authority.length() > 50) {
1370 authority = authority.substring(authority.length() - 50);
1371 }
1372 table.set(row, 0, authority, settings.syncable, settings.enabled);
1373 table.set(row, 4,
1374 status.numSourceLocal,
1375 status.numSourcePoll,
1376 status.numSourcePeriodic,
1377 status.numSourceServer,
1378 status.numSourceUser,
1379 status.numSyncs,
1380 DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
1381
1382
1383 for (int i = 0; i < settings.periodicSyncs.size(); i++) {
Matthew Williamsfa774182013-06-18 15:44:11 -07001384 final PeriodicSync sync = settings.periodicSyncs.get(i);
1385 final String period =
1386 String.format("[p:%d s, f: %d s]", sync.period, sync.flexTime);
1387 final String extras =
1388 sync.extras.size() > 0 ?
1389 sync.extras.toString() : "Bundle[]";
1390 final String next = "Next sync: " + formatTime(status.getPeriodicSyncTime(i)
1391 + sync.period * 1000);
1392 table.set(row + i * 2, 12, period + " " + extras);
Alon Albert57286f92012-10-09 14:21:38 -07001393 table.set(row + i * 2 + 1, 12, next);
1394 }
1395
1396 int row1 = row;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001397 if (settings.delayUntil > now) {
Alon Albert57286f92012-10-09 14:21:38 -07001398 table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
1399 if (settings.backoffTime > now) {
1400 table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
1401 table.set(row1++, 12, settings.backoffDelay / 1000);
1402 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001403 }
Alon Albert57286f92012-10-09 14:21:38 -07001404
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001405 if (status.lastSuccessTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001406 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
1407 + " " + "SUCCESS");
1408 table.set(row1++, 11, formatTime(status.lastSuccessTime));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001409 }
1410 if (status.lastFailureTime != 0) {
Alon Albert57286f92012-10-09 14:21:38 -07001411 table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
1412 + " " + "FAILURE");
1413 table.set(row1++, 11, formatTime(status.lastFailureTime));
1414 //noinspection UnusedAssignment
1415 table.set(row1++, 11, status.lastFailureMesg);
Dianne Hackborn231cc602009-04-27 17:10:36 -07001416 }
1417 }
Alon Albert57286f92012-10-09 14:21:38 -07001418 table.writeTo(pw);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 }
1420 }
1421
Fabrice Di Megliodc2dd882011-01-26 14:31:48 -08001422 private String getLastFailureMessage(int code) {
1423 switch (code) {
1424 case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS:
1425 return "sync already in progress";
1426
1427 case ContentResolver.SYNC_ERROR_AUTHENTICATION:
1428 return "authentication error";
1429
1430 case ContentResolver.SYNC_ERROR_IO:
1431 return "I/O error";
1432
1433 case ContentResolver.SYNC_ERROR_PARSE:
1434 return "parse error";
1435
1436 case ContentResolver.SYNC_ERROR_CONFLICT:
1437 return "conflict error";
1438
1439 case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS:
1440 return "too many deletions error";
1441
1442 case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES:
1443 return "too many retries error";
1444
1445 case ContentResolver.SYNC_ERROR_INTERNAL:
1446 return "internal error";
1447
1448 default:
1449 return "unknown";
1450 }
1451 }
1452
Dianne Hackborn231cc602009-04-27 17:10:36 -07001453 private void dumpTimeSec(PrintWriter pw, long time) {
1454 pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
1455 pw.print('s');
1456 }
Doug Zongker44f57472009-09-20 15:52:43 -07001457
Dianne Hackborn231cc602009-04-27 17:10:36 -07001458 private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
1459 pw.print("Success ("); pw.print(ds.successCount);
1460 if (ds.successCount > 0) {
1461 pw.print(" for "); dumpTimeSec(pw, ds.successTime);
1462 pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
1463 }
1464 pw.print(") Failure ("); pw.print(ds.failureCount);
1465 if (ds.failureCount > 0) {
1466 pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
1467 pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
1468 }
1469 pw.println(")");
1470 }
Doug Zongker44f57472009-09-20 15:52:43 -07001471
Alon Alberte0bde332011-09-22 14:26:16 -07001472 protected void dumpSyncHistory(PrintWriter pw) {
1473 dumpRecentHistory(pw);
1474 dumpDayStatistics(pw);
1475 }
1476
1477 private void dumpRecentHistory(PrintWriter pw) {
1478 final ArrayList<SyncStorageEngine.SyncHistoryItem> items
1479 = mSyncStorageEngine.getSyncHistory();
1480 if (items != null && items.size() > 0) {
1481 final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
1482 long totalElapsedTime = 0;
1483 long totalTimes = 0;
1484 final int N = items.size();
1485
1486 int maxAuthority = 0;
1487 int maxAccount = 0;
1488 for (SyncStorageEngine.SyncHistoryItem item : items) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001489 SyncStorageEngine.AuthorityInfo authorityInfo
Alon Alberte0bde332011-09-22 14:26:16 -07001490 = mSyncStorageEngine.getAuthority(item.authorityId);
1491 final String authorityName;
1492 final String accountKey;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001493 if (authorityInfo != null) {
Matthew Williams8ef22042013-07-26 12:56:39 -07001494 if (authorityInfo.target.target_provider) {
1495 authorityName = authorityInfo.target.provider;
1496 accountKey = authorityInfo.target.account.name + "/"
1497 + authorityInfo.target.account.type
1498 + " u" + authorityInfo.target.userId;
1499 } else if (authorityInfo.target.target_service) {
1500 authorityName = authorityInfo.target.service.getPackageName() + "/"
1501 + authorityInfo.target.service.getClassName()
1502 + " u" + authorityInfo.target.userId;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001503 accountKey = "no account";
1504 } else {
1505 authorityName = "Unknown";
1506 accountKey = "Unknown";
1507 }
Alon Alberte0bde332011-09-22 14:26:16 -07001508 } else {
1509 authorityName = "Unknown";
1510 accountKey = "Unknown";
1511 }
1512
1513 int length = authorityName.length();
1514 if (length > maxAuthority) {
1515 maxAuthority = length;
1516 }
1517 length = accountKey.length();
1518 if (length > maxAccount) {
1519 maxAccount = length;
1520 }
1521
1522 final long elapsedTime = item.elapsedTime;
1523 totalElapsedTime += elapsedTime;
1524 totalTimes++;
1525 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
1526 if (authoritySyncStats == null) {
1527 authoritySyncStats = new AuthoritySyncStats(authorityName);
1528 authorityMap.put(authorityName, authoritySyncStats);
1529 }
1530 authoritySyncStats.elapsedTime += elapsedTime;
1531 authoritySyncStats.times++;
1532 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
1533 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
1534 if (accountSyncStats == null) {
1535 accountSyncStats = new AccountSyncStats(accountKey);
1536 accountMap.put(accountKey, accountSyncStats);
1537 }
1538 accountSyncStats.elapsedTime += elapsedTime;
1539 accountSyncStats.times++;
1540
1541 }
1542
Alon Albert27096822012-01-11 18:06:41 -08001543 if (totalElapsedTime > 0) {
1544 pw.println();
1545 pw.printf("Detailed Statistics (Recent history): "
1546 + "%d (# of times) %ds (sync time)\n",
1547 totalTimes, totalElapsedTime / 1000);
Alon Alberte0bde332011-09-22 14:26:16 -07001548
Alon Albert27096822012-01-11 18:06:41 -08001549 final List<AuthoritySyncStats> sortedAuthorities =
1550 new ArrayList<AuthoritySyncStats>(authorityMap.values());
1551 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
Alon Albertbf976ba2011-10-03 13:06:43 -07001552 @Override
Alon Albert27096822012-01-11 18:06:41 -08001553 public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
Alon Albertbf976ba2011-10-03 13:06:43 -07001554 // reverse order
1555 int compare = Integer.compare(rhs.times, lhs.times);
1556 if (compare == 0) {
1557 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
Alon Alberte0bde332011-09-22 14:26:16 -07001558 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001559 return compare;
Alon Alberte0bde332011-09-22 14:26:16 -07001560 }
Alon Albertbf976ba2011-10-03 13:06:43 -07001561 });
Alon Albert27096822012-01-11 18:06:41 -08001562
1563 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
1564 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
1565 final char chars[] = new char[padLength];
1566 Arrays.fill(chars, '-');
1567 final String separator = new String(chars);
1568
1569 final String authorityFormat =
1570 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2);
1571 final String accountFormat =
1572 String.format(" %%-%ds: %%-9s %%-11s\n", maxLength);
1573
1574 pw.println(separator);
1575 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
1576 String name = authoritySyncStats.name;
1577 long elapsedTime;
1578 int times;
1579 String timeStr;
1580 String timesStr;
1581
1582 elapsedTime = authoritySyncStats.elapsedTime;
1583 times = authoritySyncStats.times;
Alon Albertbf976ba2011-10-03 13:06:43 -07001584 timeStr = String.format("%ds/%d%%",
1585 elapsedTime / 1000,
1586 elapsedTime * 100 / totalElapsedTime);
1587 timesStr = String.format("%d/%d%%",
1588 times,
1589 times * 100 / totalTimes);
Alon Albert27096822012-01-11 18:06:41 -08001590 pw.printf(authorityFormat, name, timesStr, timeStr);
1591
1592 final List<AccountSyncStats> sortedAccounts =
1593 new ArrayList<AccountSyncStats>(
1594 authoritySyncStats.accountMap.values());
1595 Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
1596 @Override
1597 public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
1598 // reverse order
1599 int compare = Integer.compare(rhs.times, lhs.times);
1600 if (compare == 0) {
1601 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
1602 }
1603 return compare;
1604 }
1605 });
1606 for (AccountSyncStats stats: sortedAccounts) {
1607 elapsedTime = stats.elapsedTime;
1608 times = stats.times;
1609 timeStr = String.format("%ds/%d%%",
1610 elapsedTime / 1000,
1611 elapsedTime * 100 / totalElapsedTime);
1612 timesStr = String.format("%d/%d%%",
1613 times,
1614 times * 100 / totalTimes);
1615 pw.printf(accountFormat, stats.name, timesStr, timeStr);
1616 }
1617 pw.println(separator);
Alon Alberte0bde332011-09-22 14:26:16 -07001618 }
Alon Alberte0bde332011-09-22 14:26:16 -07001619 }
1620
1621 pw.println();
1622 pw.println("Recent Sync History");
Alon Albert57286f92012-10-09 14:21:38 -07001623 final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n";
Alon Albertbf976ba2011-10-03 13:06:43 -07001624 final Map<String, Long> lastTimeMap = Maps.newHashMap();
Alon Albert57286f92012-10-09 14:21:38 -07001625 final PackageManager pm = mContext.getPackageManager();
Alon Alberte0bde332011-09-22 14:26:16 -07001626 for (int i = 0; i < N; i++) {
1627 SyncStorageEngine.SyncHistoryItem item = items.get(i);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001628 SyncStorageEngine.AuthorityInfo authorityInfo
Alon Alberte0bde332011-09-22 14:26:16 -07001629 = mSyncStorageEngine.getAuthority(item.authorityId);
1630 final String authorityName;
1631 final String accountKey;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001632 if (authorityInfo != null) {
Matthew Williams8ef22042013-07-26 12:56:39 -07001633 if (authorityInfo.target.target_provider) {
1634 authorityName = authorityInfo.target.provider;
1635 accountKey = authorityInfo.target.account.name + "/"
1636 + authorityInfo.target.account.type
1637 + " u" + authorityInfo.target.userId;
1638 } else if (authorityInfo.target.target_service) {
1639 authorityName = authorityInfo.target.service.getPackageName() + "/"
1640 + authorityInfo.target.service.getClassName()
1641 + " u" + authorityInfo.target.userId;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001642 accountKey = "none";
1643 } else {
1644 authorityName = "Unknown";
1645 accountKey = "Unknown";
1646 }
Alon Alberte0bde332011-09-22 14:26:16 -07001647 } else {
1648 authorityName = "Unknown";
1649 accountKey = "Unknown";
1650 }
1651 final long elapsedTime = item.elapsedTime;
1652 final Time time = new Time();
1653 final long eventTime = item.eventTime;
1654 time.set(eventTime);
1655
Alon Albertbf976ba2011-10-03 13:06:43 -07001656 final String key = authorityName + "/" + accountKey;
1657 final Long lastEventTime = lastTimeMap.get(key);
1658 final String diffString;
1659 if (lastEventTime == null) {
1660 diffString = "";
1661 } else {
1662 final long diff = (lastEventTime - eventTime) / 1000;
1663 if (diff < 60) {
1664 diffString = String.valueOf(diff);
1665 } else if (diff < 3600) {
1666 diffString = String.format("%02d:%02d", diff / 60, diff % 60);
1667 } else {
1668 final long sec = diff % 3600;
1669 diffString = String.format("%02d:%02d:%02d",
1670 diff / 3600, sec / 60, sec % 60);
1671 }
1672 }
1673 lastTimeMap.put(key, eventTime);
1674
1675 pw.printf(" #%-3d: %s %8s %5.1fs %8s",
Alon Alberte0bde332011-09-22 14:26:16 -07001676 i + 1,
1677 formatTime(eventTime),
1678 SyncStorageEngine.SOURCES[item.source],
Alon Albertbf976ba2011-10-03 13:06:43 -07001679 ((float) elapsedTime) / 1000,
1680 diffString);
Alon Albert57286f92012-10-09 14:21:38 -07001681 pw.printf(format, accountKey, authorityName,
1682 SyncOperation.reasonToString(pm, item.reason));
Alon Alberte0bde332011-09-22 14:26:16 -07001683
1684 if (item.event != SyncStorageEngine.EVENT_STOP
1685 || item.upstreamActivity != 0
1686 || item.downstreamActivity != 0) {
1687 pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n",
1688 item.event,
1689 item.upstreamActivity,
1690 item.downstreamActivity);
1691 }
1692 if (item.mesg != null
1693 && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
1694 pw.printf(" mesg=%s\n", item.mesg);
1695 }
1696 }
Alon Albert57286f92012-10-09 14:21:38 -07001697 pw.println();
1698 pw.println("Recent Sync History Extras");
1699 for (int i = 0; i < N; i++) {
1700 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
1701 final Bundle extras = item.extras;
1702 if (extras == null || extras.size() == 0) {
1703 continue;
1704 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001705 final SyncStorageEngine.AuthorityInfo authorityInfo
Alon Albert57286f92012-10-09 14:21:38 -07001706 = mSyncStorageEngine.getAuthority(item.authorityId);
1707 final String authorityName;
1708 final String accountKey;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001709 if (authorityInfo != null) {
Matthew Williams8ef22042013-07-26 12:56:39 -07001710 if (authorityInfo.target.target_provider) {
1711 authorityName = authorityInfo.target.provider;
1712 accountKey = authorityInfo.target.account.name + "/"
1713 + authorityInfo.target.account.type
1714 + " u" + authorityInfo.target.userId;
1715 } else if (authorityInfo.target.target_service) {
1716 authorityName = authorityInfo.target.service.getPackageName() + "/"
1717 + authorityInfo.target.service.getClassName()
1718 + " u" + authorityInfo.target.userId;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001719 accountKey = "none";
1720 } else {
1721 authorityName = "Unknown";
1722 accountKey = "Unknown";
1723 }
Alon Albert57286f92012-10-09 14:21:38 -07001724 } else {
1725 authorityName = "Unknown";
1726 accountKey = "Unknown";
1727 }
1728 final Time time = new Time();
1729 final long eventTime = item.eventTime;
1730 time.set(eventTime);
1731
1732 pw.printf(" #%-3d: %s %8s ",
1733 i + 1,
1734 formatTime(eventTime),
1735 SyncStorageEngine.SOURCES[item.source]);
1736
1737 pw.printf(format, accountKey, authorityName, extras);
1738 }
Alon Alberte0bde332011-09-22 14:26:16 -07001739 }
1740 }
1741
1742 private void dumpDayStatistics(PrintWriter pw) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001743 SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
1744 if (dses != null && dses[0] != null) {
1745 pw.println();
1746 pw.println("Sync Statistics");
1747 pw.print(" Today: "); dumpDayStatistic(pw, dses[0]);
1748 int today = dses[0].day;
1749 int i;
1750 SyncStorageEngine.DayStats ds;
Doug Zongker44f57472009-09-20 15:52:43 -07001751
Dianne Hackborn231cc602009-04-27 17:10:36 -07001752 // Print each day in the current week.
1753 for (i=1; i<=6 && i < dses.length; i++) {
1754 ds = dses[i];
1755 if (ds == null) break;
1756 int delta = today-ds.day;
1757 if (delta > 6) break;
Doug Zongker44f57472009-09-20 15:52:43 -07001758
Dianne Hackborn231cc602009-04-27 17:10:36 -07001759 pw.print(" Day-"); pw.print(delta); pw.print(": ");
1760 dumpDayStatistic(pw, ds);
1761 }
Doug Zongker44f57472009-09-20 15:52:43 -07001762
Dianne Hackborn231cc602009-04-27 17:10:36 -07001763 // Aggregate all following days into weeks and print totals.
1764 int weekDay = today;
1765 while (i < dses.length) {
1766 SyncStorageEngine.DayStats aggr = null;
1767 weekDay -= 7;
1768 while (i < dses.length) {
1769 ds = dses[i];
1770 if (ds == null) {
1771 i = dses.length;
1772 break;
1773 }
1774 int delta = weekDay-ds.day;
1775 if (delta > 6) break;
1776 i++;
Doug Zongker44f57472009-09-20 15:52:43 -07001777
Dianne Hackborn231cc602009-04-27 17:10:36 -07001778 if (aggr == null) {
1779 aggr = new SyncStorageEngine.DayStats(weekDay);
1780 }
1781 aggr.successCount += ds.successCount;
1782 aggr.successTime += ds.successTime;
1783 aggr.failureCount += ds.failureCount;
1784 aggr.failureTime += ds.failureTime;
1785 }
1786 if (aggr != null) {
1787 pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": ");
1788 dumpDayStatistic(pw, aggr);
1789 }
1790 }
1791 }
Alon Alberte0bde332011-09-22 14:26:16 -07001792 }
Doug Zongker44f57472009-09-20 15:52:43 -07001793
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001794 private void dumpSyncAdapters(IndentingPrintWriter pw) {
1795 pw.println();
1796 final List<UserInfo> users = getAllUsers();
1797 if (users != null) {
1798 for (UserInfo user : users) {
1799 pw.println("Sync adapters for " + user + ":");
1800 pw.increaseIndent();
1801 for (RegisteredServicesCache.ServiceInfo<?> info :
1802 mSyncAdapters.getAllServices(user.id)) {
1803 pw.println(info);
1804 }
1805 pw.decreaseIndent();
1806 pw.println();
1807 }
1808 }
1809 }
1810
Alon Alberte0bde332011-09-22 14:26:16 -07001811 private static class AuthoritySyncStats {
1812 String name;
1813 long elapsedTime;
1814 int times;
1815 Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
1816
1817 private AuthoritySyncStats(String name) {
1818 this.name = name;
1819 }
1820 }
1821
1822 private static class AccountSyncStats {
1823 String name;
1824 long elapsedTime;
1825 int times;
1826
1827 private AccountSyncStats(String name) {
1828 this.name = name;
Dianne Hackborn231cc602009-04-27 17:10:36 -07001829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 }
1831
1832 /**
1833 * A helper object to keep track of the time we have spent syncing since the last boot
1834 */
1835 private class SyncTimeTracker {
1836 /** True if a sync was in progress on the most recent call to update() */
1837 boolean mLastWasSyncing = false;
1838 /** Used to track when lastWasSyncing was last set */
1839 long mWhenSyncStarted = 0;
1840 /** The cumulative time we have spent syncing */
1841 private long mTimeSpentSyncing;
1842
1843 /** Call to let the tracker know that the sync state may have changed */
1844 public synchronized void update() {
Fred Quintana918339a2010-10-05 14:00:39 -07001845 final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 if (isSyncInProgress == mLastWasSyncing) return;
1847 final long now = SystemClock.elapsedRealtime();
1848 if (isSyncInProgress) {
1849 mWhenSyncStarted = now;
1850 } else {
1851 mTimeSpentSyncing += now - mWhenSyncStarted;
1852 }
1853 mLastWasSyncing = isSyncInProgress;
1854 }
1855
1856 /** Get how long we have been syncing, in ms */
1857 public synchronized long timeSpentSyncing() {
1858 if (!mLastWasSyncing) return mTimeSpentSyncing;
1859
1860 final long now = SystemClock.elapsedRealtime();
1861 return mTimeSpentSyncing + (now - mWhenSyncStarted);
1862 }
1863 }
1864
Fred Quintana718d8a22009-04-29 17:53:20 -07001865 class ServiceConnectionData {
1866 public final ActiveSyncContext activeSyncContext;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001867 public final IBinder adapter;
1868
1869 ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) {
Fred Quintana718d8a22009-04-29 17:53:20 -07001870 this.activeSyncContext = activeSyncContext;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001871 this.adapter = adapter;
Fred Quintana718d8a22009-04-29 17:53:20 -07001872 }
1873 }
1874
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 /**
1876 * Handles SyncOperation Messages that are posted to the associated
1877 * HandlerThread.
1878 */
1879 class SyncHandler extends Handler {
1880 // Messages that can be sent on mHandler
1881 private static final int MESSAGE_SYNC_FINISHED = 1;
1882 private static final int MESSAGE_SYNC_ALARM = 2;
1883 private static final int MESSAGE_CHECK_ALARMS = 3;
Fred Quintana718d8a22009-04-29 17:53:20 -07001884 private static final int MESSAGE_SERVICE_CONNECTED = 4;
1885 private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
Fred Quintana918339a2010-10-05 14:00:39 -07001886 private static final int MESSAGE_CANCEL = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887
1888 public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
1889 private Long mAlarmScheduleTime = null;
1890 public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001891 private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892
Matthew Williams8704fc32013-09-27 11:32:35 -07001893 private List<Message> mBootQueue = new ArrayList<Message>();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001894
Matthew Williams95906af2013-10-01 20:40:25 -07001895 public void onBootCompleted() {
Matthew Williams8704fc32013-09-27 11:32:35 -07001896 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1897 Log.v(TAG, "Boot completed, clearing boot queue.");
1898 }
Jeff Sharkey8f55d112012-10-11 11:00:21 -07001899 doDatabaseCleanup();
Matthew Williams8704fc32013-09-27 11:32:35 -07001900 synchronized(this) {
1901 // Dispatch any stashed messages.
1902 for (Message message : mBootQueue) {
1903 sendMessage(message);
1904 }
1905 mBootQueue = null;
1906 mBootCompleted = true;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001907 }
1908 }
1909
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001910 private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) {
1911 final String wakeLockKey = operation.wakeLockKey();
Fred Quintanab3029c32010-04-06 13:27:12 -07001912 PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
1913 if (wakeLock == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001914 final String name = SYNC_WAKE_LOCK_PREFIX + operation.wakeLockName();
Fred Quintanab3029c32010-04-06 13:27:12 -07001915 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
1916 wakeLock.setReferenceCounted(false);
1917 mWakeLocks.put(wakeLockKey, wakeLock);
1918 }
1919 return wakeLock;
1920 }
1921
Matthew Williams8704fc32013-09-27 11:32:35 -07001922 /**
1923 * Stash any messages that come to the handler before boot is complete.
1924 * {@link #onBootCompleted()} will disable this and dispatch all the messages collected.
1925 * @param msg Message to dispatch at a later point.
1926 * @return true if a message was enqueued, false otherwise. This is to avoid losing the
1927 * message if we manage to acquire the lock but by the time we do boot has completed.
1928 */
1929 private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
1930 synchronized (this) {
1931 if (!mBootCompleted) {
1932 // Need to copy the message bc looper will recycle it.
1933 mBootQueue.add(Message.obtain(msg));
1934 return true;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001935 }
Matthew Williams8704fc32013-09-27 11:32:35 -07001936 return false;
Fred Quintanae91ebe22009-09-29 20:44:30 -07001937 }
1938 }
Matthew Williams8704fc32013-09-27 11:32:35 -07001939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 /**
1941 * Used to keep track of whether a sync notification is active and who it is for.
1942 */
1943 class SyncNotificationInfo {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 // true iff the notification manager has been asked to send the notification
1945 public boolean isActive = false;
1946
1947 // Set when we transition from not running a sync to running a sync, and cleared on
1948 // the opposite transition.
1949 public Long startTime = null;
1950
1951 public void toString(StringBuilder sb) {
Fred Quintana918339a2010-10-05 14:00:39 -07001952 sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 }
1954
1955 @Override
1956 public String toString() {
1957 StringBuilder sb = new StringBuilder();
1958 toString(sb);
1959 return sb.toString();
1960 }
1961 }
1962
1963 public SyncHandler(Looper looper) {
1964 super(looper);
1965 }
1966
1967 public void handleMessage(Message msg) {
Matthew Williams8704fc32013-09-27 11:32:35 -07001968 if (tryEnqueueMessageUntilReadyToRun(msg)) {
1969 return;
1970 }
1971
Fred Quintana918339a2010-10-05 14:00:39 -07001972 long earliestFuturePollTime = Long.MAX_VALUE;
1973 long nextPendingSyncTime = Long.MAX_VALUE;
Subir Jhanb753c57b2011-01-10 12:09:26 -08001974 // Setting the value here instead of a method because we want the dumpsys logs
1975 // to have the most recent value used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001976 try {
Alon Albert1bad83a2011-02-16 10:29:56 -08001977 mDataConnectionIsConnected = readDataConnectionState();
Fred Quintana918339a2010-10-05 14:00:39 -07001978 mSyncManagerWakeLock.acquire();
Fred Quintana77c560f2010-03-29 22:20:26 -07001979 // Always do this first so that we be sure that any periodic syncs that
1980 // are ready to run have been converted into pending syncs. This allows the
1981 // logic that considers the next steps to take based on the set of pending syncs
1982 // to also take into account the periodic syncs.
1983 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001984 switch (msg.what) {
Fred Quintana918339a2010-10-05 14:00:39 -07001985 case SyncHandler.MESSAGE_CANCEL: {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001986 SyncStorageEngine.EndPoint payload = (SyncStorageEngine.EndPoint) msg.obj;
1987 Bundle extras = msg.peekData();
Fred Quintana918339a2010-10-05 14:00:39 -07001988 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1989 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001990 + payload + " bundle: " + extras);
Fred Quintana918339a2010-10-05 14:00:39 -07001991 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07001992 cancelActiveSyncLocked(payload, extras);
Fred Quintana918339a2010-10-05 14:00:39 -07001993 nextPendingSyncTime = maybeStartNextSyncLocked();
1994 break;
1995 }
1996
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 case SyncHandler.MESSAGE_SYNC_FINISHED:
1998 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1999 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
2000 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002001 SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj;
Fred Quintana918339a2010-10-05 14:00:39 -07002002 if (!isSyncStillActive(payload.activeSyncContext)) {
2003 Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
2004 + "sync is no longer active: "
2005 + payload.activeSyncContext);
2006 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002007 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002008 runSyncFinishedOrCanceledLocked(payload.syncResult,
2009 payload.activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010
Fred Quintana918339a2010-10-05 14:00:39 -07002011 // since a sync just finished check if it is time to start a new sync
2012 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 break;
2014
Fred Quintana718d8a22009-04-29 17:53:20 -07002015 case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002016 ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
Fred Quintana718d8a22009-04-29 17:53:20 -07002017 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2018 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07002019 + msgData.activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002020 }
2021 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07002022 if (isSyncStillActive(msgData.activeSyncContext)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002023 runBoundToAdapter(
2024 msgData.activeSyncContext,
Matthew Williams8ef22042013-07-26 12:56:39 -07002025 msgData.adapter);
Fred Quintana718d8a22009-04-29 17:53:20 -07002026 }
2027 break;
2028 }
2029
2030 case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
Fred Quintana918339a2010-10-05 14:00:39 -07002031 final ActiveSyncContext currentSyncContext =
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002032 ((ServiceConnectionData) msg.obj).activeSyncContext;
Fred Quintana718d8a22009-04-29 17:53:20 -07002033 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2034 Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
Fred Quintana918339a2010-10-05 14:00:39 -07002035 + currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002036 }
2037 // check that this isn't an old message
Fred Quintana918339a2010-10-05 14:00:39 -07002038 if (isSyncStillActive(currentSyncContext)) {
Fred Quintana718d8a22009-04-29 17:53:20 -07002039 // cancel the sync if we have a syncadapter, which means one is
2040 // outstanding
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002041 try {
2042 if (currentSyncContext.mSyncAdapter != null) {
Fred Quintana918339a2010-10-05 14:00:39 -07002043 currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002044 } else if (currentSyncContext.mSyncServiceAdapter != null) {
2045 currentSyncContext.mSyncServiceAdapter
2046 .cancelSync(currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002047 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002048 } catch (RemoteException e) {
2049 // We don't need to retry this in this case.
Fred Quintana718d8a22009-04-29 17:53:20 -07002050 }
2051
2052 // pretend that the sync failed with an IOException,
2053 // which is a soft error
2054 SyncResult syncResult = new SyncResult();
2055 syncResult.stats.numIoExceptions++;
Fred Quintana918339a2010-10-05 14:00:39 -07002056 runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002057
Fred Quintana918339a2010-10-05 14:00:39 -07002058 // since a sync just finished check if it is time to start a new sync
2059 nextPendingSyncTime = maybeStartNextSyncLocked();
Fred Quintana718d8a22009-04-29 17:53:20 -07002060 }
2061
2062 break;
2063 }
2064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002065 case SyncHandler.MESSAGE_SYNC_ALARM: {
2066 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2067 if (isLoggable) {
2068 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM");
2069 }
2070 mAlarmScheduleTime = null;
2071 try {
Fred Quintana918339a2010-10-05 14:00:39 -07002072 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002073 } finally {
2074 mHandleAlarmWakeLock.release();
2075 }
2076 break;
2077 }
2078
2079 case SyncHandler.MESSAGE_CHECK_ALARMS:
2080 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2081 Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
2082 }
Fred Quintana918339a2010-10-05 14:00:39 -07002083 nextPendingSyncTime = maybeStartNextSyncLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002084 break;
2085 }
2086 } finally {
Fred Quintana918339a2010-10-05 14:00:39 -07002087 manageSyncNotificationLocked();
2088 manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002089 mSyncTimeTracker.update();
Fred Quintana918339a2010-10-05 14:00:39 -07002090 mSyncManagerWakeLock.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002091 }
2092 }
2093
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002094 private boolean isDispatchable(SyncStorageEngine.EndPoint target) {
2095 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2096 if (target.target_provider) {
2097 // skip the sync if the account of this operation no longer exists
2098 AccountAndUser[] accounts = mRunningAccounts;
2099 if (!containsAccountAndUser(
2100 accounts, target.account, target.userId)) {
2101 return false;
2102 }
2103 if (!mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
2104 || !mSyncStorageEngine.getSyncAutomatically(
2105 target.account,
2106 target.userId,
2107 target.provider)) {
2108 if (isLoggable) {
2109 Log.v(TAG, " Not scheduling periodic operation: sync turned off.");
2110 }
2111 return false;
2112 }
2113 if (getIsSyncable(target.account, target.userId, target.provider)
2114 == 0) {
2115 if (isLoggable) {
2116 Log.v(TAG, " Not scheduling periodic operation: isSyncable == 0.");
2117 }
2118 return false;
2119 }
2120 } else if (target.target_service) {
Matthew Williams7a2ab3a2013-09-11 14:25:51 -07002121 if (mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002122 if (isLoggable) {
2123 Log.v(TAG, " Not scheduling periodic operation: isEnabled == 0.");
2124 }
2125 return false;
2126 }
2127 }
2128 return true;
2129 }
2130
Fred Quintana77c560f2010-03-29 22:20:26 -07002131 /**
2132 * Turn any periodic sync operations that are ready to run into pending sync operations.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002133 * @return the desired start time of the earliest future periodic sync operation,
Fred Quintana77c560f2010-03-29 22:20:26 -07002134 * in milliseconds since boot
2135 */
Fred Quintana918339a2010-10-05 14:00:39 -07002136 private long scheduleReadyPeriodicSyncs() {
Matthew Williamsfa774182013-06-18 15:44:11 -07002137 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2138 if (isLoggable) {
2139 Log.v(TAG, "scheduleReadyPeriodicSyncs");
2140 }
Fred Quintana918339a2010-10-05 14:00:39 -07002141 long earliestFuturePollTime = Long.MAX_VALUE;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002142
Fred Quintana77c560f2010-03-29 22:20:26 -07002143 final long nowAbsolute = System.currentTimeMillis();
Ashish Sharma69d95de2012-04-11 17:27:24 -07002144 final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002145 ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
Ashish Sharma69d95de2012-04-11 17:27:24 -07002146
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002147 ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos = mSyncStorageEngine
2148 .getCopyOfAllAuthoritiesWithSyncStatus();
2149 for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) {
2150 final AuthorityInfo authorityInfo = info.first;
2151 final SyncStatusInfo status = info.second;
Matthew Williamsd08d6682013-10-14 10:39:41 -07002152
2153 if (TextUtils.isEmpty(authorityInfo.target.provider)) {
2154 Log.e(TAG, "Got an empty provider string. Skipping: "
2155 + authorityInfo.target.provider);
Fred Quintana77c560f2010-03-29 22:20:26 -07002156 continue;
2157 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002158
Matthew Williams8ef22042013-07-26 12:56:39 -07002159 if (!isDispatchable(authorityInfo.target)) {
Fred Quintana77c560f2010-03-29 22:20:26 -07002160 continue;
2161 }
2162
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002163 for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) {
Matthew Williamsfa774182013-06-18 15:44:11 -07002164 final PeriodicSync sync = authorityInfo.periodicSyncs.get(i);
2165 final Bundle extras = sync.extras;
2166 final Long periodInMillis = sync.period * 1000;
2167 final Long flexInMillis = sync.flexTime * 1000;
2168 // Skip if the period is invalid.
Ashish Sharma49709742013-06-10 11:25:51 -07002169 if (periodInMillis <= 0) {
2170 continue;
2171 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002172 // Find when this periodic sync was last scheduled to run.
Fred Quintana77c560f2010-03-29 22:20:26 -07002173 final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
Matthew Williamsfa774182013-06-18 15:44:11 -07002174 final long shiftedLastPollTimeAbsolute =
2175 (0 < lastPollTimeAbsolute - mSyncRandomOffsetMillis) ?
2176 (lastPollTimeAbsolute - mSyncRandomOffsetMillis) : 0;
Ashish Sharma69d95de2012-04-11 17:27:24 -07002177 long remainingMillis
Matthew Williamsfa774182013-06-18 15:44:11 -07002178 = periodInMillis - (shiftedNowAbsolute % periodInMillis);
2179 long timeSinceLastRunMillis
2180 = (nowAbsolute - lastPollTimeAbsolute);
2181 // Schedule this periodic sync to run early if it's close enough to its next
2182 // runtime, and far enough from its last run time.
2183 // If we are early, there will still be time remaining in this period.
2184 boolean runEarly = remainingMillis <= flexInMillis
2185 && timeSinceLastRunMillis > periodInMillis - flexInMillis;
2186 if (isLoggable) {
Matthew Williams8ef22042013-07-26 12:56:39 -07002187 Log.v(TAG, "sync: " + i + " for " + authorityInfo.target + "."
Matthew Williamsfa774182013-06-18 15:44:11 -07002188 + " period: " + (periodInMillis)
2189 + " flex: " + (flexInMillis)
2190 + " remaining: " + (remainingMillis)
2191 + " time_since_last: " + timeSinceLastRunMillis
2192 + " last poll absol: " + lastPollTimeAbsolute
2193 + " last poll shifed: " + shiftedLastPollTimeAbsolute
2194 + " shifted now: " + shiftedNowAbsolute
2195 + " run_early: " + runEarly);
2196 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07002197 /*
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002198 * Sync scheduling strategy: Set the next periodic sync
2199 * based on a random offset (in seconds). Also sync right
2200 * now if any of the following cases hold and mark it as
2201 * having been scheduled
Matthew Williamsfa774182013-06-18 15:44:11 -07002202 * Case 1: This sync is ready to run now.
Georgi Nikolovdbe846b2013-06-25 14:09:56 -07002203 * Case 2: If the lastPollTimeAbsolute is in the
2204 * future, sync now and reinitialize. This can happen for
2205 * example if the user changed the time, synced and changed
2206 * back.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002207 * Case 3: If we failed to sync at the last scheduled time.
Matthew Williamsfa774182013-06-18 15:44:11 -07002208 * Case 4: This sync is close enough to the time that we can schedule it.
Ashish Sharma69d95de2012-04-11 17:27:24 -07002209 */
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002210 if (remainingMillis == periodInMillis // Case 1
Ashish Sharma69d95de2012-04-11 17:27:24 -07002211 || lastPollTimeAbsolute > nowAbsolute // Case 2
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002212 || timeSinceLastRunMillis >= periodInMillis // Case 3
2213 || runEarly) { // Case 4
Ashish Sharma69d95de2012-04-11 17:27:24 -07002214 // Sync now
Matthew Williams8ef22042013-07-26 12:56:39 -07002215 SyncStorageEngine.EndPoint target = authorityInfo.target;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002216 final Pair<Long, Long> backoff =
2217 mSyncStorageEngine.getBackoff(target);
Matthew Williamsfa774182013-06-18 15:44:11 -07002218 mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident,
2219 authorityInfo.periodicSyncs.get(i), nowAbsolute);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002220
2221 if (target.target_provider) {
2222 final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
2223 syncAdapterInfo = mSyncAdapters.getServiceInfo(
2224 SyncAdapterType.newKey(
2225 target.provider, target.account.type),
2226 target.userId);
2227 if (syncAdapterInfo == null) {
2228 continue;
2229 }
2230 scheduleSyncOperation(
2231 new SyncOperation(target.account, target.userId,
2232 SyncOperation.REASON_PERIODIC,
2233 SyncStorageEngine.SOURCE_PERIODIC,
2234 target.provider, extras,
2235 0 /* runtime */, 0 /* flex */,
2236 backoff != null ? backoff.first : 0,
2237 mSyncStorageEngine.getDelayUntilTime(target),
2238 syncAdapterInfo.type.allowParallelSyncs()));
2239 } else if (target.target_service) {
2240 scheduleSyncOperation(
2241 new SyncOperation(target.service, target.userId,
2242 SyncOperation.REASON_PERIODIC,
2243 SyncStorageEngine.SOURCE_PERIODIC,
2244 extras,
2245 0 /* runtime */,
2246 0 /* flex */,
2247 backoff != null ? backoff.first : 0,
2248 mSyncStorageEngine.getDelayUntilTime(target)));
2249 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07002250 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002251 // Compute when this periodic sync should next run.
2252 long nextPollTimeAbsolute;
2253 if (runEarly) {
2254 // Add the time remaining so we don't get out of phase.
2255 nextPollTimeAbsolute = nowAbsolute + periodInMillis + remainingMillis;
2256 } else {
2257 nextPollTimeAbsolute = nowAbsolute + remainingMillis;
2258 }
Ashish Sharma69d95de2012-04-11 17:27:24 -07002259 if (nextPollTimeAbsolute < earliestFuturePollTime) {
2260 earliestFuturePollTime = nextPollTimeAbsolute;
Fred Quintana77c560f2010-03-29 22:20:26 -07002261 }
2262 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002263 }
2264
Fred Quintana918339a2010-10-05 14:00:39 -07002265 if (earliestFuturePollTime == Long.MAX_VALUE) {
2266 return Long.MAX_VALUE;
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002267 }
2268
Fred Quintana77c560f2010-03-29 22:20:26 -07002269 // convert absolute time to elapsed time
Matthew Williamsfa774182013-06-18 15:44:11 -07002270 return SystemClock.elapsedRealtime() +
2271 ((earliestFuturePollTime < nowAbsolute) ?
2272 0 : (earliestFuturePollTime - nowAbsolute));
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08002273 }
2274
Fred Quintana918339a2010-10-05 14:00:39 -07002275 private long maybeStartNextSyncLocked() {
2276 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2277 if (isLoggable) Log.v(TAG, "maybeStartNextSync");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278
2279 // If we aren't ready to run (e.g. the data connection is down), get out.
2280 if (!mDataConnectionIsConnected) {
2281 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002282 Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 }
Fred Quintana918339a2010-10-05 14:00:39 -07002284 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002285 }
2286
2287 if (mStorageIsLow) {
2288 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002289 Log.v(TAG, "maybeStartNextSync: memory low, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002290 }
Fred Quintana918339a2010-10-05 14:00:39 -07002291 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002292 }
2293
2294 // If the accounts aren't known yet then we aren't ready to run. We will be kicked
2295 // when the account lookup request does complete.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002296 if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002297 if (isLoggable) {
Fred Quintana918339a2010-10-05 14:00:39 -07002298 Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002299 }
Fred Quintana918339a2010-10-05 14:00:39 -07002300 return Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002301 }
2302
2303 // Otherwise consume SyncOperations from the head of the SyncQueue until one is
2304 // found that is runnable (not disabled, etc). If that one is ready to run then
2305 // start it, otherwise just get out.
Fred Quintana918339a2010-10-05 14:00:39 -07002306 final long now = SystemClock.elapsedRealtime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002307
Fred Quintana918339a2010-10-05 14:00:39 -07002308 // will be set to the next time that a sync should be considered for running
2309 long nextReadyToRunTime = Long.MAX_VALUE;
2310
2311 // order the sync queue, dropping syncs that are not allowed
2312 ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
2313 synchronized (mSyncQueue) {
2314 if (isLoggable) {
2315 Log.v(TAG, "build the operation array, syncQueue size is "
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002316 + mSyncQueue.getOperations().size());
Fred Quintana918339a2010-10-05 14:00:39 -07002317 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002318 final Iterator<SyncOperation> operationIterator =
2319 mSyncQueue.getOperations().iterator();
Alon Albert8e285552012-09-17 15:05:27 -07002320
2321 final ActivityManager activityManager
2322 = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
2323 final Set<Integer> removedUsers = Sets.newHashSet();
Fred Quintana918339a2010-10-05 14:00:39 -07002324 while (operationIterator.hasNext()) {
2325 final SyncOperation op = operationIterator.next();
Fred Quintana307da1a2010-01-21 14:24:20 -08002326
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002327 // If the user is not running, skip the request.
2328 if (!activityManager.isUserRunning(op.target.userId)) {
2329 final UserInfo userInfo = mUserManager.getUserInfo(op.target.userId);
Alon Albert8e285552012-09-17 15:05:27 -07002330 if (userInfo == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002331 removedUsers.add(op.target.userId);
Alon Albert8e285552012-09-17 15:05:27 -07002332 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002333 if (isLoggable) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002334 Log.v(TAG, " Dropping all sync operations for + "
2335 + op.target.userId + ": user not running.");
Fred Quintana918339a2010-10-05 14:00:39 -07002336 }
Fred Quintana77c560f2010-03-29 22:20:26 -07002337 continue;
2338 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002339 if (!isOperationValidLocked(op)) {
2340 operationIterator.remove();
2341 mSyncStorageEngine.deleteFromPending(op.pendingOperation);
2342 continue;
2343 }
Matthew Williamsfa774182013-06-18 15:44:11 -07002344 // If the next run time is in the future, even given the flexible scheduling,
2345 // return the time.
2346 if (op.effectiveRunTime - op.flexTime > now) {
2347 if (nextReadyToRunTime > op.effectiveRunTime) {
2348 nextReadyToRunTime = op.effectiveRunTime;
2349 }
2350 if (isLoggable) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002351 Log.v(TAG, " Not running sync operation: Sync too far in future."
2352 + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime
2353 + " now: " + now);
Matthew Williamsfa774182013-06-18 15:44:11 -07002354 }
2355 continue;
2356 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002357 // Add this sync to be run.
Fred Quintana918339a2010-10-05 14:00:39 -07002358 operations.add(op);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002359 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002360
Alon Albert8e285552012-09-17 15:05:27 -07002361 for (Integer user : removedUsers) {
2362 // if it's still removed
2363 if (mUserManager.getUserInfo(user) == null) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002364 onUserRemoved(user);
Alon Albert8e285552012-09-17 15:05:27 -07002365 }
2366 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002367 }
2368
Fred Quintana918339a2010-10-05 14:00:39 -07002369 // find the next operation to dispatch, if one is ready
Matthew Williamsfa774182013-06-18 15:44:11 -07002370 // iterate from the top, keep issuing (while potentially canceling existing syncs)
Fred Quintana918339a2010-10-05 14:00:39 -07002371 // until the quotas are filled.
2372 // once the quotas are filled iterate once more to find when the next one would be
2373 // (also considering pre-emption reasons).
2374 if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
2375 Collections.sort(operations);
2376 if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
2377 for (int i = 0, N = operations.size(); i < N; i++) {
2378 final SyncOperation candidate = operations.get(i);
2379 final boolean candidateIsInitialization = candidate.isInitialization();
2380
2381 int numInit = 0;
2382 int numRegular = 0;
2383 ActiveSyncContext conflict = null;
2384 ActiveSyncContext longRunning = null;
2385 ActiveSyncContext toReschedule = null;
Fred Quintanadc475562012-05-04 15:51:54 -07002386 ActiveSyncContext oldestNonExpeditedRegular = null;
Fred Quintana918339a2010-10-05 14:00:39 -07002387
2388 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2389 final SyncOperation activeOp = activeSyncContext.mSyncOperation;
2390 if (activeOp.isInitialization()) {
2391 numInit++;
2392 } else {
2393 numRegular++;
Fred Quintanadc475562012-05-04 15:51:54 -07002394 if (!activeOp.isExpedited()) {
2395 if (oldestNonExpeditedRegular == null
2396 || (oldestNonExpeditedRegular.mStartTime
2397 > activeSyncContext.mStartTime)) {
2398 oldestNonExpeditedRegular = activeSyncContext;
2399 }
2400 }
Fred Quintana918339a2010-10-05 14:00:39 -07002401 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002402 if (activeOp.isConflict(candidate)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002403 conflict = activeSyncContext;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002404 // don't break out since we want to do a full count of the varieties.
Fred Quintana918339a2010-10-05 14:00:39 -07002405 } else {
2406 if (candidateIsInitialization == activeOp.isInitialization()
2407 && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
2408 longRunning = activeSyncContext;
2409 // don't break out since we want to do a full count of the varieties
2410 }
2411 }
2412 }
2413
2414 if (isLoggable) {
2415 Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
2416 Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
2417 Log.v(TAG, " longRunning: " + longRunning);
2418 Log.v(TAG, " conflict: " + conflict);
Fred Quintanadc475562012-05-04 15:51:54 -07002419 Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002420 }
2421
Fred Quintanadc475562012-05-04 15:51:54 -07002422 final boolean roomAvailable = candidateIsInitialization
2423 ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
2424 : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
2425
Fred Quintana918339a2010-10-05 14:00:39 -07002426 if (conflict != null) {
2427 if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
2428 && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
2429 toReschedule = conflict;
2430 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2431 Log.v(TAG, "canceling and rescheduling sync since an initialization "
2432 + "takes higher priority, " + conflict);
2433 }
2434 } else if (candidate.expedited && !conflict.mSyncOperation.expedited
2435 && (candidateIsInitialization
2436 == conflict.mSyncOperation.isInitialization())) {
2437 toReschedule = conflict;
2438 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2439 Log.v(TAG, "canceling and rescheduling sync since an expedited "
2440 + "takes higher priority, " + conflict);
2441 }
2442 } else {
2443 continue;
2444 }
Fred Quintanadc475562012-05-04 15:51:54 -07002445 } else if (roomAvailable) {
2446 // dispatch candidate
2447 } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null
2448 && !candidateIsInitialization) {
2449 // We found an active, non-expedited regular sync. We also know that the
2450 // candidate doesn't conflict with this active sync since conflict
2451 // is null. Reschedule the active sync and start the candidate.
2452 toReschedule = oldestNonExpeditedRegular;
2453 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002454 Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to"
2455 + " run, " + oldestNonExpeditedRegular);
Fred Quintana918339a2010-10-05 14:00:39 -07002456 }
Fred Quintanadc475562012-05-04 15:51:54 -07002457 } else if (longRunning != null
2458 && (candidateIsInitialization
2459 == longRunning.mSyncOperation.isInitialization())) {
2460 // We found an active, long-running sync. Reschedule the active
2461 // sync and start the candidate.
2462 toReschedule = longRunning;
2463 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2464 Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
2465 + longRunning);
2466 }
2467 } else {
2468 // we were unable to find or make space to run this candidate, go on to
2469 // the next one
2470 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002471 }
2472
2473 if (toReschedule != null) {
2474 runSyncFinishedOrCanceledLocked(null, toReschedule);
2475 scheduleSyncOperation(toReschedule.mSyncOperation);
2476 }
Jeff Sharkeya706e2f2012-10-16 12:02:42 -07002477 synchronized (mSyncQueue) {
Fred Quintana918339a2010-10-05 14:00:39 -07002478 mSyncQueue.remove(candidate);
2479 }
Fred Quintana87b14662011-09-12 10:32:55 -07002480 dispatchSyncOperation(candidate);
Fred Quintana918339a2010-10-05 14:00:39 -07002481 }
2482
2483 return nextReadyToRunTime;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002484 }
2485
2486 /**
2487 * Determine if a sync is no longer valid and should be dropped from the sync queue and its
2488 * pending op deleted.
2489 * @param op operation for which the sync is to be scheduled.
2490 */
2491 private boolean isOperationValidLocked(SyncOperation op) {
2492 final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2493 int targetUid;
2494 int state;
2495 final SyncStorageEngine.EndPoint target = op.target;
2496 boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId);
2497 if (target.target_provider) {
2498 // Drop the sync if the account of this operation no longer exists.
2499 AccountAndUser[] accounts = mRunningAccounts;
2500 if (!containsAccountAndUser(accounts, target.account, target.userId)) {
2501 if (isLoggable) {
2502 Log.v(TAG, " Dropping sync operation: account doesn't exist.");
2503 }
2504 return false;
2505 }
2506 // Drop this sync request if it isn't syncable.
2507 state = getIsSyncable(target.account, target.userId, target.provider);
2508 if (state == 0) {
2509 if (isLoggable) {
2510 Log.v(TAG, " Dropping sync operation: isSyncable == 0.");
2511 }
2512 return false;
2513 }
2514 syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically(
2515 target.account, target.userId, target.provider);
2516
2517 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2518 syncAdapterInfo = mSyncAdapters.getServiceInfo(
2519 SyncAdapterType.newKey(
2520 target.provider, target.account.type), target.userId);
2521 if (syncAdapterInfo != null) {
2522 targetUid = syncAdapterInfo.uid;
2523 } else {
2524 if (isLoggable) {
2525 Log.v(TAG, " Dropping sync operation: No sync adapter registered"
2526 + "for: " + target);
2527 }
2528 return false;
2529 }
2530 } else if (target.target_service) {
Matthew Williams7a2ab3a2013-09-11 14:25:51 -07002531 state = mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)
2532 ? 1 : 0;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002533 if (state == 0) {
2534 // TODO: Change this to not drop disabled syncs - keep them in the pending queue.
2535 if (isLoggable) {
2536 Log.v(TAG, " Dropping sync operation: isActive == 0.");
2537 }
2538 return false;
2539 }
2540 try {
2541 targetUid = mContext.getPackageManager()
2542 .getServiceInfo(target.service, 0)
2543 .applicationInfo
2544 .uid;
2545 } catch (PackageManager.NameNotFoundException e) {
2546 if (isLoggable) {
2547 Log.v(TAG, " Dropping sync operation: No service registered for: "
2548 + target.service);
2549 }
2550 return false;
2551 }
2552 } else {
2553 Log.e(TAG, "Unknown target for Sync Op: " + target);
2554 return false;
2555 }
2556
2557 // We ignore system settings that specify the sync is invalid if:
2558 // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
2559 // or
2560 // 2) it's an initialisation sync - we just need to connect to it.
2561 final boolean ignoreSystemConfiguration =
2562 op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
2563 || (state < 0);
2564
2565 // Sync not enabled.
2566 if (!syncEnabled && !ignoreSystemConfiguration) {
2567 if (isLoggable) {
2568 Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
2569 }
2570 return false;
2571 }
2572 // Network down.
2573 final NetworkInfo networkInfo = getConnectivityManager()
2574 .getActiveNetworkInfoForUid(targetUid);
2575 final boolean uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
2576 if (!uidNetworkConnected && !ignoreSystemConfiguration) {
2577 if (isLoggable) {
2578 Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
2579 }
2580 return false;
2581 }
2582 // Metered network.
2583 if (op.isNotAllowedOnMetered() && getConnectivityManager().isActiveNetworkMetered()
2584 && !ignoreSystemConfiguration) {
2585 if (isLoggable) {
2586 Log.v(TAG, " Dropping sync operation: not allowed on metered network.");
2587 }
2588 return false;
2589 }
2590 return true;
2591 }
Fred Quintana918339a2010-10-05 14:00:39 -07002592
Fred Quintana87b14662011-09-12 10:32:55 -07002593 private boolean dispatchSyncOperation(SyncOperation op) {
Fred Quintana918339a2010-10-05 14:00:39 -07002594 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana0c4d04a2010-11-03 17:02:55 -07002595 Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
Fred Quintana918339a2010-10-05 14:00:39 -07002596 Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
2597 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
2598 Log.v(TAG, syncContext.toString());
2599 }
2600 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002601 // Connect to the sync adapter.
2602 int targetUid;
2603 ComponentName targetComponent;
2604 final SyncStorageEngine.EndPoint info = op.target;
2605 if (info.target_provider) {
2606 SyncAdapterType syncAdapterType =
2607 SyncAdapterType.newKey(info.provider, info.account.type);
2608 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2609 syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
2610 if (syncAdapterInfo == null) {
2611 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
2612 + ", removing settings for it");
2613 mSyncStorageEngine.removeAuthority(info);
2614 return false;
2615 }
2616 targetUid = syncAdapterInfo.uid;
2617 targetComponent = syncAdapterInfo.componentName;
2618 } else {
2619 // TODO: Store the uid of the service as part of the authority info in order to
2620 // avoid this call?
2621 try {
2622 targetUid = mContext.getPackageManager()
2623 .getServiceInfo(info.service, 0)
2624 .applicationInfo
2625 .uid;
2626 targetComponent = info.service;
2627 } catch(PackageManager.NameNotFoundException e) {
2628 Log.d(TAG, "Can't find a service for " + info.service
2629 + ", removing settings for it");
2630 mSyncStorageEngine.removeAuthority(info);
2631 return false;
2632 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 }
Fred Quintana718d8a22009-04-29 17:53:20 -07002634 ActiveSyncContext activeSyncContext =
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002635 new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
Fred Quintana918339a2010-10-05 14:00:39 -07002636 activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
2637 mActiveSyncContexts.add(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002638 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana918339a2010-10-05 14:00:39 -07002639 Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002640 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002641 if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
2642 Log.e(TAG, "Bind attempt failed - target: " + targetComponent);
Fred Quintana918339a2010-10-05 14:00:39 -07002643 closeActiveSyncContext(activeSyncContext);
Fred Quintana87b14662011-09-12 10:32:55 -07002644 return false;
Fred Quintana718d8a22009-04-29 17:53:20 -07002645 }
2646
Fred Quintana87b14662011-09-12 10:32:55 -07002647 return true;
Fred Quintana718d8a22009-04-29 17:53:20 -07002648 }
2649
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002650 private void runBoundToAdapter(final ActiveSyncContext activeSyncContext,
Matthew Williams8ef22042013-07-26 12:56:39 -07002651 IBinder syncAdapter) {
Fred Quintana918339a2010-10-05 14:00:39 -07002652 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
Fred Quintana718d8a22009-04-29 17:53:20 -07002653 try {
Alon Alberteca75112010-12-08 15:02:33 -08002654 activeSyncContext.mIsLinkedToDeath = true;
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002655 syncAdapter.linkToDeath(activeSyncContext, 0);
Alon Alberteca75112010-12-08 15:02:33 -08002656
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002657 if (syncOperation.target.target_provider) {
2658 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
2659 activeSyncContext.mSyncAdapter
2660 .startSync(activeSyncContext, syncOperation.target.provider,
2661 syncOperation.target.account, syncOperation.extras);
2662 } else if (syncOperation.target.target_service) {
2663 activeSyncContext.mSyncServiceAdapter =
2664 ISyncServiceAdapter.Stub.asInterface(syncAdapter);
2665 activeSyncContext.mSyncServiceAdapter
2666 .startSync(activeSyncContext, syncOperation.extras);
2667 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002668 } catch (RemoteException remoteExc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002669 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
2670 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002671 increaseBackoffSetting(syncOperation);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002672 scheduleSyncOperation(
2673 new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 } catch (RuntimeException exc) {
Fred Quintana918339a2010-10-05 14:00:39 -07002675 closeActiveSyncContext(activeSyncContext);
Fred Quintana307da1a2010-01-21 14:24:20 -08002676 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002677 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002678 }
2679
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002680 /**
Matthew Williams8ef22042013-07-26 12:56:39 -07002681 * Cancel the sync for the provided target that matches the given bundle.
2682 * @param info can have null fields to indicate all the active syncs for that field.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002683 */
2684 private void cancelActiveSyncLocked(SyncStorageEngine.EndPoint info, Bundle extras) {
Fred Quintana918339a2010-10-05 14:00:39 -07002685 ArrayList<ActiveSyncContext> activeSyncs =
2686 new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
2687 for (ActiveSyncContext activeSyncContext : activeSyncs) {
2688 if (activeSyncContext != null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002689 final SyncStorageEngine.EndPoint opInfo =
2690 activeSyncContext.mSyncOperation.target;
Matthew Williams8ef22042013-07-26 12:56:39 -07002691 if (!opInfo.matchesSpec(info)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002692 continue;
Fred Quintana918339a2010-10-05 14:00:39 -07002693 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002694 if (extras != null &&
2695 !syncExtrasEquals(activeSyncContext.mSyncOperation.extras,
2696 extras,
2697 false /* no config settings */)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002698 continue;
2699 }
Fred Quintana918339a2010-10-05 14:00:39 -07002700 runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
2701 activeSyncContext);
2702 }
2703 }
2704 }
2705
2706 private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
2707 ActiveSyncContext activeSyncContext) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002708 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Alon Alberteca75112010-12-08 15:02:33 -08002709
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002710 final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
2711 final SyncStorageEngine.EndPoint info = syncOperation.target;
2712
Alon Alberteca75112010-12-08 15:02:33 -08002713 if (activeSyncContext.mIsLinkedToDeath) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002714 if (info.target_provider) {
2715 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
2716 } else {
Matthew Williams8ef22042013-07-26 12:56:39 -07002717 activeSyncContext.mSyncServiceAdapter.asBinder()
2718 .unlinkToDeath(activeSyncContext, 0);
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002719 }
Alon Alberteca75112010-12-08 15:02:33 -08002720 activeSyncContext.mIsLinkedToDeath = false;
2721 }
Fred Quintana918339a2010-10-05 14:00:39 -07002722 closeActiveSyncContext(activeSyncContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002724 String historyMessage;
2725 int downstreamActivity;
2726 int upstreamActivity;
2727 if (syncResult != null) {
2728 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002729 Log.v(TAG, "runSyncFinishedOrCanceled [finished]: "
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002730 + syncOperation + ", result " + syncResult);
2731 }
2732
2733 if (!syncResult.hasError()) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002734 historyMessage = SyncStorageEngine.MESG_SUCCESS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002735 // TODO: set these correctly when the SyncResult is extended to include it
2736 downstreamActivity = 0;
2737 upstreamActivity = 0;
Alon Albert6e079a32010-11-12 12:41:09 -08002738 clearBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002739 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -08002740 Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
2741 // the operation failed so increase the backoff time
2742 if (!syncResult.syncAlreadyInProgress) {
2743 increaseBackoffSetting(syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002744 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002745 // reschedule the sync if so indicated by the syncResult
2746 maybeRescheduleSync(syncResult, syncOperation);
Alon Albert57286f92012-10-09 14:21:38 -07002747 historyMessage = ContentResolver.syncErrorToString(
2748 syncResultToErrorNumber(syncResult));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 // TODO: set these correctly when the SyncResult is extended to include it
2750 downstreamActivity = 0;
2751 upstreamActivity = 0;
2752 }
Fred Quintana307da1a2010-01-21 14:24:20 -08002753
2754 setDelayUntilTime(syncOperation, syncResult.delayUntil);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002755 } else {
2756 if (isLoggable) {
Fred Quintana307da1a2010-01-21 14:24:20 -08002757 Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002758 }
Fred Quintana718d8a22009-04-29 17:53:20 -07002759 if (activeSyncContext.mSyncAdapter != null) {
2760 try {
Fred Quintana21bb0de2009-06-16 10:24:58 -07002761 activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
Fred Quintana718d8a22009-04-29 17:53:20 -07002762 } catch (RemoteException e) {
2763 // we don't need to retry this in this case
2764 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002765 } else if (activeSyncContext.mSyncServiceAdapter != null) {
2766 try {
2767 activeSyncContext.mSyncServiceAdapter.cancelSync(activeSyncContext);
2768 } catch (RemoteException e) {
2769 // we don't need to retry this in this case
2770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 }
Dianne Hackborn231cc602009-04-27 17:10:36 -07002772 historyMessage = SyncStorageEngine.MESG_CANCELED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002773 downstreamActivity = 0;
2774 upstreamActivity = 0;
2775 }
2776
2777 stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
2778 upstreamActivity, downstreamActivity, elapsedTime);
2779
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002780 // Check for full-resync and schedule it after closing off the last sync.
2781 if (info.target_provider) {
2782 if (syncResult != null && syncResult.tooManyDeletions) {
2783 installHandleTooManyDeletesNotification(info.account,
2784 info.provider, syncResult.stats.numDeletes,
2785 info.userId);
2786 } else {
2787 mNotificationMgr.cancelAsUser(null,
2788 info.account.hashCode() ^ info.provider.hashCode(),
2789 new UserHandle(info.userId));
2790 }
2791 if (syncResult != null && syncResult.fullSyncRequested) {
2792 scheduleSyncOperation(
2793 new SyncOperation(info.account, info.userId,
2794 syncOperation.reason,
2795 syncOperation.syncSource, info.provider, new Bundle(),
2796 0 /* delay */, 0 /* flex */,
2797 syncOperation.backoff, syncOperation.delayUntil,
2798 syncOperation.allowParallelSyncs));
2799 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002800 } else {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002801 if (syncResult != null && syncResult.fullSyncRequested) {
2802 scheduleSyncOperation(
2803 new SyncOperation(info.service, info.userId,
2804 syncOperation.reason,
2805 syncOperation.syncSource, new Bundle(),
2806 0 /* delay */, 0 /* flex */,
2807 syncOperation.backoff, syncOperation.delayUntil));
2808 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002809 }
2810 // no need to schedule an alarm, as that will be done by our caller.
2811 }
2812
Fred Quintana918339a2010-10-05 14:00:39 -07002813 private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
2814 activeSyncContext.close();
2815 mActiveSyncContexts.remove(activeSyncContext);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002816 mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
Matthew Williams56dbf8f2013-07-26 12:56:39 -07002817 activeSyncContext.mSyncOperation.target.userId);
Fred Quintana918339a2010-10-05 14:00:39 -07002818 }
2819
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002820 /**
2821 * Convert the error-containing SyncResult into the Sync.History error number. Since
2822 * the SyncResult may indicate multiple errors at once, this method just returns the
2823 * most "serious" error.
2824 * @param syncResult the SyncResult from which to read
2825 * @return the most "serious" error set in the SyncResult
2826 * @throws IllegalStateException if the SyncResult does not indicate any errors.
2827 * If SyncResult.error() is true then it is safe to call this.
2828 */
2829 private int syncResultToErrorNumber(SyncResult syncResult) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07002830 if (syncResult.syncAlreadyInProgress)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002831 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002832 if (syncResult.stats.numAuthExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002833 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002834 if (syncResult.stats.numIoExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002835 return ContentResolver.SYNC_ERROR_IO;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002836 if (syncResult.stats.numParseExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002837 return ContentResolver.SYNC_ERROR_PARSE;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002838 if (syncResult.stats.numConflictDetectedExceptions > 0)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002839 return ContentResolver.SYNC_ERROR_CONFLICT;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002840 if (syncResult.tooManyDeletions)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002841 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002842 if (syncResult.tooManyRetries)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002843 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
Dianne Hackborn231cc602009-04-27 17:10:36 -07002844 if (syncResult.databaseError)
Fred Quintanaac9385e2009-06-22 18:00:59 -07002845 return ContentResolver.SYNC_ERROR_INTERNAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002846 throw new IllegalStateException("we are not in an error state, " + syncResult);
2847 }
2848
Fred Quintana918339a2010-10-05 14:00:39 -07002849 private void manageSyncNotificationLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002850 boolean shouldCancel;
2851 boolean shouldInstall;
2852
Fred Quintana918339a2010-10-05 14:00:39 -07002853 if (mActiveSyncContexts.isEmpty()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854 mSyncNotificationInfo.startTime = null;
2855
2856 // we aren't syncing. if the notification is active then remember that we need
2857 // to cancel it and then clear out the info
2858 shouldCancel = mSyncNotificationInfo.isActive;
2859 shouldInstall = false;
2860 } else {
2861 // we are syncing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002862 final long now = SystemClock.elapsedRealtime();
2863 if (mSyncNotificationInfo.startTime == null) {
2864 mSyncNotificationInfo.startTime = now;
2865 }
2866
Fred Quintana918339a2010-10-05 14:00:39 -07002867 // there are three cases:
2868 // - the notification is up: do nothing
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002869 // - the notification is not up but it isn't time yet: don't install
2870 // - the notification is not up and it is time: need to install
2871
2872 if (mSyncNotificationInfo.isActive) {
Fred Quintana918339a2010-10-05 14:00:39 -07002873 shouldInstall = shouldCancel = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 } else {
Fred Quintana918339a2010-10-05 14:00:39 -07002875 // it isn't currently up, so there is nothing to cancel
2876 shouldCancel = false;
2877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002878 final boolean timeToShowNotification =
2879 now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
Fred Quintana918339a2010-10-05 14:00:39 -07002880 if (timeToShowNotification) {
2881 shouldInstall = true;
2882 } else {
2883 // show the notification immediately if this is a manual sync
2884 shouldInstall = false;
2885 for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
2886 final boolean manualSync = activeSyncContext.mSyncOperation.extras
2887 .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
2888 if (manualSync) {
2889 shouldInstall = true;
2890 break;
2891 }
2892 }
2893 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002894 }
2895 }
2896
2897 if (shouldCancel && !shouldInstall) {
2898 mNeedSyncActiveNotification = false;
2899 sendSyncStateIntent();
2900 mSyncNotificationInfo.isActive = false;
2901 }
2902
2903 if (shouldInstall) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002904 mNeedSyncActiveNotification = true;
2905 sendSyncStateIntent();
2906 mSyncNotificationInfo.isActive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002907 }
2908 }
2909
Fred Quintana918339a2010-10-05 14:00:39 -07002910 private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
2911 long nextPendingEventElapsedTime) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002912 // in each of these cases the sync loop will be kicked, which will cause this
2913 // method to be called again
2914 if (!mDataConnectionIsConnected) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915 if (mStorageIsLow) return;
2916
Fred Quintana918339a2010-10-05 14:00:39 -07002917 // When the status bar notification should be raised
2918 final long notificationTime =
2919 (!mSyncHandler.mSyncNotificationInfo.isActive
2920 && mSyncHandler.mSyncNotificationInfo.startTime != null)
2921 ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY
2922 : Long.MAX_VALUE;
Fred Quintana307da1a2010-01-21 14:24:20 -08002923
Fred Quintana918339a2010-10-05 14:00:39 -07002924 // When we should consider canceling an active sync
2925 long earliestTimeoutTime = Long.MAX_VALUE;
2926 for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
2927 final long currentSyncTimeoutTime =
2928 currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
2929 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2930 Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
2931 + currentSyncTimeoutTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002932 }
Fred Quintana918339a2010-10-05 14:00:39 -07002933 if (earliestTimeoutTime > currentSyncTimeoutTime) {
2934 earliestTimeoutTime = currentSyncTimeoutTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002935 }
2936 }
2937
Fred Quintana918339a2010-10-05 14:00:39 -07002938 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2939 Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime);
2940 }
2941
2942 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2943 Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
2944 }
2945
2946 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2947 Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
2948 + nextPeriodicEventElapsedTime);
2949 }
2950 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2951 Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
2952 + nextPendingEventElapsedTime);
2953 }
2954
2955 long alarmTime = Math.min(notificationTime, earliestTimeoutTime);
2956 alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime);
2957 alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
2958
2959 // Bound the alarm time.
2960 final long now = SystemClock.elapsedRealtime();
2961 if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
2962 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2963 Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
2964 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 }
Fred Quintana918339a2010-10-05 14:00:39 -07002966 alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
2967 } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) {
2968 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2969 Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, "
2970 + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
2971 }
2972 alarmTime = now + SYNC_ALARM_TIMEOUT_MAX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002973 }
2974
2975 // determine if we need to set or cancel the alarm
2976 boolean shouldSet = false;
2977 boolean shouldCancel = false;
Matthew Williamsba352712013-08-13 15:53:31 -07002978 final boolean alarmIsActive = (mAlarmScheduleTime != null) && (now < mAlarmScheduleTime);
Fred Quintana918339a2010-10-05 14:00:39 -07002979 final boolean needAlarm = alarmTime != Long.MAX_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002980 if (needAlarm) {
Matthew Williamsba352712013-08-13 15:53:31 -07002981 // Need the alarm if
2982 // - it's currently not set
2983 // - if the alarm is set in the past.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002984 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) {
2985 shouldSet = true;
2986 }
2987 } else {
2988 shouldCancel = alarmIsActive;
2989 }
2990
Matthew Williamsfa774182013-06-18 15:44:11 -07002991 // Set or cancel the alarm as directed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002992 ensureAlarmService();
2993 if (shouldSet) {
Fred Quintana918339a2010-10-05 14:00:39 -07002994 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2995 Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
2996 + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
2997 + " secs from now");
2998 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002999 mAlarmScheduleTime = alarmTime;
Matthew Williamsba352712013-08-13 15:53:31 -07003000 mAlarmService.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003001 mSyncAlarmIntent);
3002 } else if (shouldCancel) {
3003 mAlarmScheduleTime = null;
3004 mAlarmService.cancel(mSyncAlarmIntent);
3005 }
3006 }
3007
3008 private void sendSyncStateIntent() {
3009 Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07003010 syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003011 syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
Fred Quintana918339a2010-10-05 14:00:39 -07003012 syncStateIntent.putExtra("failing", false);
Amith Yamasanicd757062012-10-19 18:23:52 -07003013 mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003014 }
3015
Fred Quintanad9d2f112009-04-23 13:36:27 -07003016 private void installHandleTooManyDeletesNotification(Account account, String authority,
Dianne Hackborn41203752012-08-31 14:05:51 -07003017 long numDeletes, int userId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003018 if (mNotificationMgr == null) return;
Fred Quintanac848b702009-08-25 20:18:46 -07003019
3020 final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
3021 authority, 0 /* flags */);
3022 if (providerInfo == null) {
3023 return;
3024 }
3025 CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
3026
Fred Quintanab19e62a2010-12-16 13:54:43 -08003027 Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003028 clickIntent.putExtra("account", account);
Tadashi G. Takaoka86135d32009-09-24 19:31:44 -07003029 clickIntent.putExtra("authority", authority);
Fred Quintanac848b702009-08-25 20:18:46 -07003030 clickIntent.putExtra("provider", authorityName.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 clickIntent.putExtra("numDeletes", numDeletes);
3032
3033 if (!isActivityAvailable(clickIntent)) {
3034 Log.w(TAG, "No activity found to handle too many deletes.");
3035 return;
3036 }
3037
3038 final PendingIntent pendingIntent = PendingIntent
Dianne Hackborn41203752012-08-31 14:05:51 -07003039 .getActivityAsUser(mContext, 0, clickIntent,
3040 PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003041
3042 CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
3043 R.string.contentServiceTooManyDeletesNotificationDesc);
3044
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003045 Notification notification =
3046 new Notification(R.drawable.stat_notify_sync_error,
3047 mContext.getString(R.string.contentServiceSync),
3048 System.currentTimeMillis());
3049 notification.setLatestEventInfo(mContext,
3050 mContext.getString(R.string.contentServiceSyncNotificationTitle),
Fred Quintanac848b702009-08-25 20:18:46 -07003051 String.format(tooManyDeletesDescFormat.toString(), authorityName),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003052 pendingIntent);
3053 notification.flags |= Notification.FLAG_ONGOING_EVENT;
Dianne Hackborn41203752012-08-31 14:05:51 -07003054 mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(),
3055 notification, new UserHandle(userId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003056 }
3057
3058 /**
3059 * Checks whether an activity exists on the system image for the given intent.
3060 *
3061 * @param intent The intent for an activity.
3062 * @return Whether or not an activity exists.
3063 */
3064 private boolean isActivityAvailable(Intent intent) {
3065 PackageManager pm = mContext.getPackageManager();
3066 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
3067 int listSize = list.size();
3068 for (int i = 0; i < listSize; i++) {
3069 ResolveInfo resolveInfo = list.get(i);
3070 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3071 != 0) {
3072 return true;
3073 }
3074 }
3075
3076 return false;
3077 }
3078
3079 public long insertStartSyncEvent(SyncOperation syncOperation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003080 final long now = System.currentTimeMillis();
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003081 EventLog.writeEvent(2720,
3082 syncOperation.toEventLog(SyncStorageEngine.EVENT_START));
3083 return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003084 }
3085
3086 public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
3087 int upstreamActivity, int downstreamActivity, long elapsedTime) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003088 EventLog.writeEvent(2720,
3089 syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
Fred Quintana77c560f2010-03-29 22:20:26 -07003090 mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08003091 resultMessage, downstreamActivity, upstreamActivity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003092 }
3093 }
Fred Quintana918339a2010-10-05 14:00:39 -07003094
3095 private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
3096 for (ActiveSyncContext sync : mActiveSyncContexts) {
3097 if (sync == activeSyncContext) {
3098 return true;
3099 }
3100 }
3101 return false;
3102 }
Alon Albert57286f92012-10-09 14:21:38 -07003103
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003104 /**
3105 * Sync extra comparison function.
3106 * @param b1 bundle to compare
3107 * @param b2 other bundle to compare
3108 * @param includeSyncSettings if false, ignore system settings in bundle.
3109 */
3110 public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) {
3111 if (b1 == b2) {
3112 return true;
3113 }
Matthew Williams8ef22042013-07-26 12:56:39 -07003114 // Exit early if we can.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003115 if (includeSyncSettings && b1.size() != b2.size()) {
3116 return false;
3117 }
Matthew Williams8ef22042013-07-26 12:56:39 -07003118 Bundle bigger = b1.size() > b2.size() ? b1 : b2;
3119 Bundle smaller = b1.size() > b2.size() ? b2 : b1;
3120 for (String key : bigger.keySet()) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003121 if (!includeSyncSettings && isSyncSetting(key)) {
3122 continue;
3123 }
Matthew Williams8ef22042013-07-26 12:56:39 -07003124 if (!smaller.containsKey(key)) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003125 return false;
3126 }
Matthew Williams8ef22042013-07-26 12:56:39 -07003127 if (!bigger.get(key).equals(smaller.get(key))) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003128 return false;
3129 }
3130 }
3131 return true;
3132 }
3133
3134 /**
Matthew Williams8ef22042013-07-26 12:56:39 -07003135 * TODO: Get rid of this when we separate sync settings extras from dev specified extras.
Matthew Williams56dbf8f2013-07-26 12:56:39 -07003136 * @return true if the provided key is used by the SyncManager in scheduling the sync.
3137 */
3138 private static boolean isSyncSetting(String key) {
3139 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
3140 return true;
3141 }
3142 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
3143 return true;
3144 }
3145 if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) {
3146 return true;
3147 }
3148 if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) {
3149 return true;
3150 }
3151 if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) {
3152 return true;
3153 }
3154 if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
3155 return true;
3156 }
3157 if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) {
3158 return true;
3159 }
3160 if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) {
3161 return true;
3162 }
3163 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) {
3164 return true;
3165 }
3166 if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) {
3167 return true;
3168 }
3169 if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) {
3170 return true;
3171 }
3172 if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) {
3173 return true;
3174 }
3175 if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
3176 return true;
3177 }
3178 return false;
3179 }
3180
Alon Albert57286f92012-10-09 14:21:38 -07003181 static class PrintTable {
3182 private ArrayList<Object[]> mTable = Lists.newArrayList();
3183 private final int mCols;
3184
3185 PrintTable(int cols) {
3186 mCols = cols;
3187 }
3188
3189 void set(int row, int col, Object... values) {
3190 if (col + values.length > mCols) {
3191 throw new IndexOutOfBoundsException("Table only has " + mCols +
3192 " columns. can't set " + values.length + " at column " + col);
3193 }
3194 for (int i = mTable.size(); i <= row; i++) {
3195 final Object[] list = new Object[mCols];
3196 mTable.add(list);
3197 for (int j = 0; j < mCols; j++) {
3198 list[j] = "";
3199 }
3200 }
3201 System.arraycopy(values, 0, mTable.get(row), col, values.length);
3202 }
3203
3204 void writeTo(PrintWriter out) {
3205 final String[] formats = new String[mCols];
3206 int totalLength = 0;
3207 for (int col = 0; col < mCols; ++col) {
3208 int maxLength = 0;
3209 for (Object[] row : mTable) {
3210 final int length = row[col].toString().length();
3211 if (length > maxLength) {
3212 maxLength = length;
3213 }
3214 }
3215 totalLength += maxLength;
3216 formats[col] = String.format("%%-%ds", maxLength);
3217 }
Patrick Tjin31068162014-01-30 13:28:46 -08003218 formats[mCols - 1] = "%s";
Alon Albert57286f92012-10-09 14:21:38 -07003219 printRow(out, formats, mTable.get(0));
3220 totalLength += (mCols - 1) * 2;
3221 for (int i = 0; i < totalLength; ++i) {
3222 out.print("-");
3223 }
3224 out.println();
3225 for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
3226 Object[] row = mTable.get(i);
3227 printRow(out, formats, row);
3228 }
3229 }
3230
3231 private void printRow(PrintWriter out, String[] formats, Object[] row) {
3232 for (int j = 0, rowLength = row.length; j < rowLength; j++) {
3233 out.printf(String.format(formats[j], row[j].toString()));
3234 out.print(" ");
3235 }
3236 out.println();
3237 }
3238
3239 public int getNumRows() {
3240 return mTable.size();
3241 }
3242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003243}